diff options
| author | Quentin Colombet <qcolombet@apple.com> | 2016-03-12 02:25:27 +0000 |
|---|---|---|
| committer | Quentin Colombet <qcolombet@apple.com> | 2016-03-12 02:25:27 +0000 |
| commit | cf9732b4177bba5a073619f8f067cf4a0193b325 (patch) | |
| tree | 45ace4c06425bbf240c1d70151ff2760e320e92f /llvm/lib/Target/X86/X86InstrCompiler.td | |
| parent | 39a02a7beddbb720c3e9eda82e5e007b5c6738a0 (diff) | |
| download | bcm5719-llvm-cf9732b4177bba5a073619f8f067cf4a0193b325.tar.gz bcm5719-llvm-cf9732b4177bba5a073619f8f067cf4a0193b325.zip | |
[X86] Make sure we do not clobber RBX with cmpxchg when used as a base pointer.
cmpxchg[8|16]b uses RBX as one of its argument.
In other words, using this instruction clobbers RBX as it is defined to hold one
the input. When the backend uses dynamically allocated stack, RBX is used as a
reserved register for the base pointer.
Reserved registers have special semantic that only the target understands and
enforces, because of that, the register allocator don’t use them, but also,
don’t try to make sure they are used properly (remember it does not know how
they are supposed to be used).
Therefore, when RBX is used as a reserved register but defined by something that
is not compatible with that use, the register allocator will not fix the
surrounding code to make sure it gets saved and restored properly around the
broken code. This is the responsibility of the target to do the right thing with
its reserved register.
To fix that, when the base pointer needs to be preserved, we use a different
pseudo instruction for cmpxchg that save rbx.
That pseudo takes two more arguments than the regular instruction:
- One is the value to be copied into RBX to set the proper value for the
comparison.
- The other is the virtual register holding the save of the value of RBX as the
base pointer. This saving is done as part of isel (i.e., we emit a copy from
rbx).
cmpxchg_save_rbx <regular cmpxchg args>, input_for_rbx_reg, save_of_rbx_as_bp
This gets expanded into:
rbx = copy input_for_rbx_reg
cmpxchg <regular cmpxchg args>
rbx = save_of_rbx_as_bp
Note: The actual modeling of the pseudo is a bit more complicated to make sure
the interferes that appears after the pseudo gets expanded are properly modeled
before that expansion.
This fixes PR26883.
llvm-svn: 263325
Diffstat (limited to 'llvm/lib/Target/X86/X86InstrCompiler.td')
| -rw-r--r-- | llvm/lib/Target/X86/X86InstrCompiler.td | 46 |
1 files changed, 46 insertions, 0 deletions
diff --git a/llvm/lib/Target/X86/X86InstrCompiler.td b/llvm/lib/Target/X86/X86InstrCompiler.td index 6ed62a00451..2c92cc370b5 100644 --- a/llvm/lib/Target/X86/X86InstrCompiler.td +++ b/llvm/lib/Target/X86/X86InstrCompiler.td @@ -737,6 +737,38 @@ defm LCMPXCHG8B : LCMPXCHG_UnOp<0xC7, MRM1m, "cmpxchg8b", IIC_CMPX_LOCK_8B>; } +// This pseudo must be used when the frame uses RBX as +// the base pointer. Indeed, in such situation RBX is a reserved +// register and the register allocator will ignore any use/def of +// it. In other words, the register will not fix the clobbering of +// RBX that will happen when setting the arguments for the instrucion. +// +// Unlike the actual related instuction, we mark that this one +// defines EBX (instead of using EBX). +// The rationale is that we will define RBX during the expansion of +// the pseudo. The argument feeding EBX is ebx_input. +// +// The additional argument, $ebx_save, is a temporary register used to +// save the value of RBX accross the actual instruction. +// +// To make sure the register assigned to $ebx_save does not interfere with +// the definition of the actual instruction, we use a definition $dst which +// is tied to $rbx_save. That way, the live-range of $rbx_save spans accross +// the instruction and we are sure we will have a valid register to restore +// the value of RBX. +let Defs = [EAX, EDX, EBX, EFLAGS], Uses = [EAX, ECX, EDX], + SchedRW = [WriteALULd, WriteRMW], isCodeGenOnly = 1, isPseudo = 1, + Constraints = "$ebx_save = $dst", usesCustomInserter = 1 in { +def LCMPXCHG8B_SAVE_EBX : + I<0, Pseudo, (outs GR32:$dst), + (ins i64mem:$ptr, GR32:$ebx_input, GR32:$ebx_save), + !strconcat("cmpxchg8b", "\t$ptr"), + [(set GR32:$dst, (X86cas8save_ebx addr:$ptr, GR32:$ebx_input, + GR32:$ebx_save))], + IIC_CMPX_LOCK_8B>; +} + + let Defs = [RAX, RDX, EFLAGS], Uses = [RAX, RBX, RCX, RDX], Predicates = [HasCmpxchg16b], SchedRW = [WriteALULd, WriteRMW] in { defm LCMPXCHG16B : LCMPXCHG_UnOp<0xC7, MRM1m, "cmpxchg16b", @@ -744,6 +776,20 @@ defm LCMPXCHG16B : LCMPXCHG_UnOp<0xC7, MRM1m, "cmpxchg16b", IIC_CMPX_LOCK_16B>, REX_W; } +// Same as LCMPXCHG8B_SAVE_RBX but for the 16 Bytes variant. +let Defs = [RAX, RDX, RBX, EFLAGS], Uses = [RAX, RCX, RDX], + Predicates = [HasCmpxchg16b], SchedRW = [WriteALULd, WriteRMW], + isCodeGenOnly = 1, isPseudo = 1, Constraints = "$rbx_save = $dst", + usesCustomInserter = 1 in { +def LCMPXCHG16B_SAVE_RBX : + I<0, Pseudo, (outs GR64:$dst), + (ins i128mem:$ptr, GR64:$rbx_input, GR64:$rbx_save), + !strconcat("cmpxchg16b", "\t$ptr"), + [(set GR64:$dst, (X86cas16save_rbx addr:$ptr, GR64:$rbx_input, + GR64:$rbx_save))], + IIC_CMPX_LOCK_16B>; +} + defm LCMPXCHG : LCMPXCHG_BinOp<0xB0, 0xB1, MRMDestMem, "cmpxchg", X86cas, IIC_CMPX_LOCK_8, IIC_CMPX_LOCK>; |

