diff --git a/src/reg-alloc.c b/src/reg-alloc.c index 3e38c15c..77a23652 100644 --- a/src/reg-alloc.c +++ b/src/reg-alloc.c @@ -986,6 +986,19 @@ int prepare_operand(basic_block_t *bb, var_t *var, int operand_0) return i; } + /* The reload below reads the variable's slot, so there has to be one + * holding its value. A parameter arrives in a register and is given a + * slot only when something spills it: reloading before that read whatever + * the frame happened to hold, and "int x = a - b; int *p = &b;" computed + * a - garbage. Writing the register back first is what makes the slot + * stand for the variable. + */ + if (i > -1 && var->address_taken && !var->space_is_allocated) { + store_var(bb, var, i); + vreg_map_to_phys(var, i); + return i; + } + for (i = 0; i < REG_CNT; i++) { if (reg_is_free(i)) { load_var(bb, var, i); diff --git a/tests/driver.sh b/tests/driver.sh index 0f3afd66..ed994e5a 100755 --- a/tests/driver.sh +++ b/tests/driver.sh @@ -477,6 +477,7 @@ begin_category "Standalone Programs" "Testing checked-in end-to-end programs" try_file 0 'F(10) = 55' "$TESTS_DIR/fib.c" try_file 0 $'1\nHello World' "$TESTS_DIR/hello.c" try_file 0 '' "$TESTS_DIR/strength-reduce.c" +try_file 0 '' "$TESTS_DIR/escaped-param.c" # Category: Basic Literals and Constants begin_category "Literals and Constants" "Testing integer, character, and string literals" diff --git a/tests/escaped-param.c b/tests/escaped-param.c new file mode 100644 index 00000000..e367b0bd --- /dev/null +++ b/tests/escaped-param.c @@ -0,0 +1,42 @@ +/* prepare_operand() reloads an address-taken variable from its stack slot + * rather than reading the register it is already in, because a store through + * the pointer would not have gone to the register. That reload needs the slot + * to hold the value, and a parameter arrives in a register with no slot at + * all: one is reserved the first time something spills it. + * + * Taking the address after the expression is what orders the two so the + * reload comes first. Nothing has to be stored through the pointer -- the + * value is already wrong by the time the address is taken. + */ + +int sub_then_escape(int a, int b) +{ + int x = a - b; + int *p = &b; + return x; +} + +int add_then_escape(int a, int b) +{ + int x = a + b; + int *p = &b; + return x; +} + +int escape_first_operand(int a, int b) +{ + int x = a - b; + int *p = &a; + return x; +} + +int main() +{ + if (sub_then_escape(5, 2) != 3) + return 1; + if (add_then_escape(1, 2) != 3) + return 2; + if (escape_first_operand(5, 2) != 3) + return 3; + return 0; +}