From: Matthias Braun Date: Mon, 29 Jun 2015 21:35:51 +0000 (+0000) Subject: X86: Rework inline asm integer register specification. X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=7839b00d43d9163fe4f2c8e9a3b59965fcd8d600;p=oota-llvm.git X86: Rework inline asm integer register specification. This is a new version of http://reviews.llvm.org/D10260. It turned out that when you specify an integer register in inline asm on x86 you get the register of the required type size back. That means that X86TargetLowering::getRegForInlineAsmConstraint() has to accept any of the integer registers and adapt its size to the given target size which may be any 8/16/32/64 bit sized type. Surprisingly that means given a constraint of "{ax}" and a type of MVT::F32 we need to return X86::EAX. This change makes this face explicit, the previous code seemed like working by accident because there it never returned an error once a register was found. On the other hand this rewrite allows to actually return errors for invalid situations like requesting an integer register for an i128 type. Related to rdar://21042280 Differential Revision: http://reviews.llvm.org/D10813 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@241002 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Target/X86/X86ISelLowering.cpp b/lib/Target/X86/X86ISelLowering.cpp index 33b01e007db..888affb17e5 100644 --- a/lib/Target/X86/X86ISelLowering.cpp +++ b/lib/Target/X86/X86ISelLowering.cpp @@ -25580,71 +25580,40 @@ X86TargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI, // Otherwise, check to see if this is a register class of the wrong value // type. For example, we want to map "{ax},i32" -> {eax}, we don't want it to // turn into {ax},{dx}. - if (Res.second->hasType(VT)) + // MVT::Other is used to specify clobber names. + if (Res.second->hasType(VT) || VT == MVT::Other) return Res; // Correct type already, nothing to do. - // All of the single-register GCC register classes map their values onto - // 16-bit register pieces "ax","dx","cx","bx","si","di","bp","sp". If we - // really want an 8-bit or 32-bit register, map to the appropriate register - // class and return the appropriate register. - if (Res.second == &X86::GR16RegClass) { - if (VT == MVT::i8 || VT == MVT::i1) { - unsigned DestReg = 0; - switch (Res.first) { - default: break; - case X86::AX: DestReg = X86::AL; break; - case X86::DX: DestReg = X86::DL; break; - case X86::CX: DestReg = X86::CL; break; - case X86::BX: DestReg = X86::BL; break; - } - if (DestReg) { - Res.first = DestReg; - Res.second = &X86::GR8RegClass; - } - } else if (VT == MVT::i32 || VT == MVT::f32) { - unsigned DestReg = 0; - switch (Res.first) { - default: break; - case X86::AX: DestReg = X86::EAX; break; - case X86::DX: DestReg = X86::EDX; break; - case X86::CX: DestReg = X86::ECX; break; - case X86::BX: DestReg = X86::EBX; break; - case X86::SI: DestReg = X86::ESI; break; - case X86::DI: DestReg = X86::EDI; break; - case X86::BP: DestReg = X86::EBP; break; - case X86::SP: DestReg = X86::ESP; break; - } - if (DestReg) { - Res.first = DestReg; - Res.second = &X86::GR32RegClass; - } - } else if (VT == MVT::i64 || VT == MVT::f64) { - unsigned DestReg = 0; - switch (Res.first) { - default: break; - case X86::AX: DestReg = X86::RAX; break; - case X86::DX: DestReg = X86::RDX; break; - case X86::CX: DestReg = X86::RCX; break; - case X86::BX: DestReg = X86::RBX; break; - case X86::SI: DestReg = X86::RSI; break; - case X86::DI: DestReg = X86::RDI; break; - case X86::BP: DestReg = X86::RBP; break; - case X86::SP: DestReg = X86::RSP; break; - } - if (DestReg) { - Res.first = DestReg; - Res.second = &X86::GR64RegClass; - } - } - } else if (Res.second == &X86::FR32RegClass || - Res.second == &X86::FR64RegClass || - Res.second == &X86::VR128RegClass || - Res.second == &X86::VR256RegClass || - Res.second == &X86::FR32XRegClass || - Res.second == &X86::FR64XRegClass || - Res.second == &X86::VR128XRegClass || - Res.second == &X86::VR256XRegClass || - Res.second == &X86::VR512RegClass) { + // Get a matching integer of the correct size. i.e. "ax" with MVT::32 should + // return "eax". This should even work for things like getting 64bit integer + // registers when given an f64 type. + const TargetRegisterClass *Class = Res.second; + if (Class == &X86::GR8RegClass || Class == &X86::GR16RegClass || + Class == &X86::GR32RegClass || Class == &X86::GR64RegClass) { + unsigned Size = VT.getSizeInBits(); + MVT::SimpleValueType SimpleTy = Size == 1 || Size == 8 ? MVT::i8 + : Size == 16 ? MVT::i16 + : Size == 32 ? MVT::i32 + : Size == 64 ? MVT::i64 + : MVT::Other; + unsigned DestReg = getX86SubSuperRegisterOrZero(Res.first, SimpleTy); + if (DestReg > 0) { + Res.first = DestReg; + Res.second = SimpleTy == MVT::i8 ? &X86::GR8RegClass + : SimpleTy == MVT::i16 ? &X86::GR16RegClass + : SimpleTy == MVT::i32 ? &X86::GR32RegClass + : &X86::GR64RegClass; + assert(Res.second->contains(Res.first) && "Register in register class"); + } else { + // No register found/type mismatch. + Res.first = 0; + Res.second = nullptr; + } + } else if (Class == &X86::FR32RegClass || Class == &X86::FR64RegClass || + Class == &X86::VR128RegClass || Class == &X86::VR256RegClass || + Class == &X86::FR32XRegClass || Class == &X86::FR64XRegClass || + Class == &X86::VR128XRegClass || Class == &X86::VR256XRegClass || + Class == &X86::VR512RegClass) { // Handle references to XMM physical registers that got mapped into the // wrong class. This can happen with constraints like {xmm0} where the // target independent register mapper will just pick the first match it can @@ -25660,6 +25629,11 @@ X86TargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI, Res.second = &X86::VR256RegClass; else if (X86::VR512RegClass.hasType(VT)) Res.second = &X86::VR512RegClass; + else { + // Type mismatch and not a clobber: Return an error; + Res.first = 0; + Res.second = nullptr; + } } return Res; diff --git a/lib/Target/X86/X86RegisterInfo.cpp b/lib/Target/X86/X86RegisterInfo.cpp index 72703a848f8..0033b505818 100644 --- a/lib/Target/X86/X86RegisterInfo.cpp +++ b/lib/Target/X86/X86RegisterInfo.cpp @@ -598,10 +598,10 @@ X86RegisterInfo::getPtrSizedFrameRegister(const MachineFunction &MF) const { } namespace llvm { -unsigned getX86SubSuperRegister(unsigned Reg, MVT::SimpleValueType VT, - bool High) { +unsigned getX86SubSuperRegisterOrZero(unsigned Reg, MVT::SimpleValueType VT, + bool High) { switch (VT) { - default: llvm_unreachable("Unexpected VT"); + default: return 0; case MVT::i8: if (High) { switch (Reg) { @@ -625,7 +625,7 @@ unsigned getX86SubSuperRegister(unsigned Reg, MVT::SimpleValueType VT, } } else { switch (Reg) { - default: llvm_unreachable("Unexpected register"); + default: return 0; case X86::AH: case X86::AL: case X86::AX: case X86::EAX: case X86::RAX: return X86::AL; case X86::DH: case X86::DL: case X86::DX: case X86::EDX: case X86::RDX: @@ -662,7 +662,7 @@ unsigned getX86SubSuperRegister(unsigned Reg, MVT::SimpleValueType VT, } case MVT::i16: switch (Reg) { - default: llvm_unreachable("Unexpected register"); + default: return 0; case X86::AH: case X86::AL: case X86::AX: case X86::EAX: case X86::RAX: return X86::AX; case X86::DH: case X86::DL: case X86::DX: case X86::EDX: case X86::RDX: @@ -698,7 +698,7 @@ unsigned getX86SubSuperRegister(unsigned Reg, MVT::SimpleValueType VT, } case MVT::i32: switch (Reg) { - default: llvm_unreachable("Unexpected register"); + default: return 0; case X86::AH: case X86::AL: case X86::AX: case X86::EAX: case X86::RAX: return X86::EAX; case X86::DH: case X86::DL: case X86::DX: case X86::EDX: case X86::RDX: @@ -734,7 +734,7 @@ unsigned getX86SubSuperRegister(unsigned Reg, MVT::SimpleValueType VT, } case MVT::i64: switch (Reg) { - default: llvm_unreachable("Unexpected register"); + default: return 0; case X86::AH: case X86::AL: case X86::AX: case X86::EAX: case X86::RAX: return X86::RAX; case X86::DH: case X86::DL: case X86::DX: case X86::EDX: case X86::RDX: @@ -771,6 +771,14 @@ unsigned getX86SubSuperRegister(unsigned Reg, MVT::SimpleValueType VT, } } +unsigned getX86SubSuperRegister(unsigned Reg, MVT::SimpleValueType VT, + bool High) { + unsigned Res = getX86SubSuperRegisterOrZero(Reg, VT, High); + if (Res == 0) + llvm_unreachable("Unexpected register or VT"); + return Res; +} + unsigned get512BitSuperRegister(unsigned Reg) { if (Reg >= X86::XMM0 && Reg <= X86::XMM31) return X86::ZMM0 + (Reg - X86::XMM0); diff --git a/lib/Target/X86/X86RegisterInfo.h b/lib/Target/X86/X86RegisterInfo.h index b754cadd0eb..8de1d0bf8ec 100644 --- a/lib/Target/X86/X86RegisterInfo.h +++ b/lib/Target/X86/X86RegisterInfo.h @@ -128,11 +128,16 @@ public: unsigned getSlotSize() const { return SlotSize; } }; -// getX86SubSuperRegister - X86 utility function. It returns the sub or super -// register of a specific X86 register. -// e.g. getX86SubSuperRegister(X86::EAX, MVT::i16) return X86:AX +/// Returns the sub or super register of a specific X86 register. +/// e.g. getX86SubSuperRegister(X86::EAX, MVT::i16) returns X86::AX. +/// Aborts on error. unsigned getX86SubSuperRegister(unsigned, MVT::SimpleValueType, bool High=false); +/// Returns the sub or super register of a specific X86 register. +/// Like getX86SubSuperRegister() but returns 0 on error. +unsigned getX86SubSuperRegisterOrZero(unsigned, MVT::SimpleValueType, + bool High = false); + //get512BitRegister - X86 utility - returns 512-bit super register unsigned get512BitSuperRegister(unsigned Reg); diff --git a/test/CodeGen/X86/asm-mismatched-types.ll b/test/CodeGen/X86/asm-mismatched-types.ll new file mode 100644 index 00000000000..97f9c0872f8 --- /dev/null +++ b/test/CodeGen/X86/asm-mismatched-types.ll @@ -0,0 +1,135 @@ +; RUN: llc -o - %s -no-integrated-as | FileCheck %s +target triple = "x86_64--" + +; Allow to specify any of the 8/16/32/64 register names interchangeably in +; constraints + +; Produced by C-programs like this: +; void foo(int p) { register int reg __asm__("r8") = p; +; __asm__ __volatile__("# REG: %0" : : "r" (reg)); } + +; CHECK-LABEL: reg64_as_32: +; CHECK: # REG: %r8d +define void @reg64_as_32(i32 %p) { + call void asm sideeffect "# REG: $0", "{r8}"(i32 %p) + ret void +} + +; CHECK-LABEL: reg64_as_32_float: +; CHECK: # REG: %r8d +define void @reg64_as_32_float(float %p) { + call void asm sideeffect "# REG: $0", "{r8}"(float %p) + ret void +} + +; CHECK-LABEL: reg64_as_16: +; CHECK: # REG: %r9w +define void @reg64_as_16(i16 %p) { + call void asm sideeffect "# REG: $0", "{r9}"(i16 %p) + ret void +} + +; CHECK-LABEL: reg64_as_8: +; CHECK: # REG: %bpl +define void @reg64_as_8(i8 %p) { + call void asm sideeffect "# REG: $0", "{rbp}"(i8 %p) + ret void +} + +; CHECK-LABEL: reg32_as_16: +; CHECK: # REG: %r15w +define void @reg32_as_16(i16 %p) { + call void asm sideeffect "# REG: $0", "{r15d}"(i16 %p) + ret void +} + +; CHECK-LABEL: reg32_as_8: +; CHECK: # REG: %r12b +define void @reg32_as_8(i8 %p) { + call void asm sideeffect "# REG: $0", "{r12d}"(i8 %p) + ret void +} + +; CHECK-LABEL: reg16_as_8: +; CHECK: # REG: %cl +define void @reg16_as_8(i8 %p) { + call void asm sideeffect "# REG: $0", "{cx}"(i8 %p) + ret void +} + +; CHECK-LABEL: reg32_as_64: +; CHECK: # REG: %rbp +define void @reg32_as_64(i64 %p) { + call void asm sideeffect "# REG: $0", "{ebp}"(i64 %p) + ret void +} + +; CHECK-LABEL: reg32_as_64_float: +; CHECK: # REG: %rbp +define void @reg32_as_64_float(double %p) { + call void asm sideeffect "# REG: $0", "{ebp}"(double %p) + ret void +} + +; CHECK-LABEL: reg16_as_64: +; CHECK: # REG: %r13 +define void @reg16_as_64(i64 %p) { + call void asm sideeffect "# REG: $0", "{r13w}"(i64 %p) + ret void +} + +; CHECK-LABEL: reg16_as_64_float: +; CHECK: # REG: %r13 +define void @reg16_as_64_float(double %p) { + call void asm sideeffect "# REG: $0", "{r13w}"(double %p) + ret void +} + +; CHECK-LABEL: reg8_as_64: +; CHECK: # REG: %rax +define void @reg8_as_64(i64 %p) { + call void asm sideeffect "# REG: $0", "{al}"(i64 %p) + ret void +} + +; CHECK-LABEL: reg8_as_64_float: +; CHECK: # REG: %rax +define void @reg8_as_64_float(double %p) { + call void asm sideeffect "# REG: $0", "{al}"(double %p) + ret void +} + +; CHECK-LABEL: reg16_as_32: +; CHECK: # REG: %r11d +define void @reg16_as_32(i32 %p) { + call void asm sideeffect "# REG: $0", "{r11w}"(i32 %p) + ret void +} + +; CHECK-LABEL: reg16_as_32_float: +; CHECK: # REG: %r11d +define void @reg16_as_32_float(float %p) { + call void asm sideeffect "# REG: $0", "{r11w}"(float %p) + ret void +} + +; CHECK-LABEL: reg8_as_32: +; CHECK: # REG: %r9d +define void @reg8_as_32(i32 %p) { + call void asm sideeffect "# REG: $0", "{r9b}"(i32 %p) + ret void +} + +; CHECK-LABEL: reg8_as_32_float: +; CHECK: # REG: %r9d +define void @reg8_as_32_float(float %p) { + call void asm sideeffect "# REG: $0", "{r9b}"(float %p) + ret void +} + +; CHECK-LABEL: reg8_as_16: +; CHECK: # REG: %di +define void @reg8_as_16(i16 %p) { + call void asm sideeffect "# REG: $0", "{dil}"(i16 %p) + ret void +} diff --git a/test/CodeGen/X86/asm-reject-reg-type-mismatch.ll b/test/CodeGen/X86/asm-reject-reg-type-mismatch.ll new file mode 100644 index 00000000000..c7e86f565ee --- /dev/null +++ b/test/CodeGen/X86/asm-reject-reg-type-mismatch.ll @@ -0,0 +1,8 @@ +; RUN: not llc -o /dev/null %s 2>&1 | FileCheck %s +target triple = "x86_64--" + +; CHECK: error: couldn't allocate output register for constraint '{ax}' +define i128 @blup() { + %v = tail call i128 asm "", "={ax},0"(i128 0) + ret i128 %v +}