Prevent hoisting the instruction whose def might be clobbered by the terminator

Sasa Stankovic Sasa.Stankovic at imgtec.com
Wed May 28 14:14:52 PDT 2014


Hi,

Attached is the patch that checks for registers implicitly defined by the terminator, to prevent hoisting the instruction whose def might be clobbered by the terminator.

The problem that the attached patch fixes was encountered while stress testing MIPS long branches on LLVM test suite (description of MIPS long branches, needed to better understand why the test failed is given below). We passed the MIPS-specific command line option to llc that expands all direct branches to long branches.

The situation that caused test to fail is as follows. We have 3 blocks (say B1, B2 and B3), where B1 is the only predecessor of B2 and B3, and there is identical code at the start of B2 and B3. This identical code is removed from B2 and B3, and moved to B1.

The problem is that instruction that defines $at was moved to B1, but the instruction that uses $at was left in B2. So when the branch at the end of B1 is expanded to long branch, it clobbers $at and B2 will use the wrong value of $at.

Here is a short description of MIPS long branches, and how register $at is used. MIPS has direct branch instructions with the 18-bit offset. When the actual branch offset cannot fit into 18-bit field, MipsLongBranch pass (run as the last pass before code emission) replaces every such direct branch with the following long branch sequence:

1:   $longbr:
2:       addiu $sp, $sp, -8
3:       sw $ra, 0($sp)
4:       lui $at, %hi($tgt - $baltgt)
5:       bal $baltgt
6:       addiu $at, $at, %lo($tgt - $baltgt)
7:   $baltgt:
8:       addu $at, $ra, $at
9:       lw $ra, 0($sp)
10:      addiu $sp, $sp, 8
11:      jr $at
12:  $fallthrough:

If you are not familiar with the MIPS assembly, here is what this code does. First the stack pointer $sp is incremented by 8 bytes and the original return address $ra is saved on stack. The instructions in lines 4 and 6 load $at register with the value $tgt - $baltgt, which is the offset of the branch target $tgt from the label $baltgt. BAL instruction is a function call and it jumps to the label $baltgt; after the call is executed the return address register $ra has the value $baltgt (note that MIPS has branch delay slots, so that return address is the second instruction following the branch). ADDU instruction adds "$baltgt" and "$tgt - $baltgt", so that $at will contain the address of branch target $tgt. At the end $ra is restored from stack, stack pointer is also restored, and the code jumps to the target basic block through the indirect jump "jr $at".

This code uses registers $ra and $at. $ra is saved on stack and restored, but we cannot do the same for $at since the code jumps to target through $at. So register $at is always clobbered by the long branch. To prevent that this affects the surrounding code, MIPS direct branches implicitly define register $at (that is, tablegen definitions for MIPS direct branches specify that they define register $at). This ensures that virtual register defined in one block and used in successor block won't be allocated in register $at, thus making it safe to expand ordinary branch to long branch.

Regards,
Sasa



More information about the llvm-commits mailing list