[llvm] 2fe3151 - [X86] TCRETURNmi fix for 32bit platform
Phoebe Wang via llvm-commits
llvm-commits at lists.llvm.org
Wed Feb 9 04:34:15 PST 2022
Author: Tong Zhang
Date: 2022-02-09T20:34:04+08:00
New Revision: 2fe315162e44084004827e70dcc31c21b1ebd181
URL: https://github.com/llvm/llvm-project/commit/2fe315162e44084004827e70dcc31c21b1ebd181
DIFF: https://github.com/llvm/llvm-project/commit/2fe315162e44084004827e70dcc31c21b1ebd181.diff
LOG: [X86] TCRETURNmi fix for 32bit platform
This fix is similar to 3cf3ffce240e("Fix the TCRETURNmi64 bug differently.")
after allocating register for index+base, we will only have one register left
This bug affects linux kernel compilation for x86 target. Error happens when compiling kmod_si476x_core.
clang complains:
error: ran out of registers during register allocation
The full command is:
clang -Wp,-MMD,drivers/mfd/.si476x-cmd.o.d -nostdinc -isystem /opt/toolchain/main/lib/clang/14.0.0/include -I./arch/x86/include -I./arch/x86/include/generated -I./include -I./arch/x86/include/uapi -I./arch/x86/include/generated/uapi -I./include/uapi -I./include/generated/uapi -include ./include/linux/compiler-version.h -include ./include/linux/kconfig.h -include ./include/linux/compiler_types.h -D__KERNEL__ -Qunused-arguments -fmacro-prefix-map=./= -Wall -Wundef -Werror=strict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE -Werror=implicit-function-declaration -Werror=implicit-int -Werror=return-type -Wno-format-security -std=gnu89 -no-integrated-as --prefix=/usr/bin/ -Werror=unknown-warning-option -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx -fcf-protection=none -m32 -msoft-float -mregparm=3 -freg-struct-return -fno-pic -mstack-alignment=4 -march=atom -mtune=atom -mtune=generic -Wa,-mtune=generic32 -ffreestanding -Wno-sign-compare -fno-asynchronous-unwind-tables -fno-delete-null-pointer-checks -Wno-frame-address -Wno-address-of-packed-member -O2 -Wframe-larger-than=1024 -fno-stack-protector -Wno-format-invalid-specifier -Wno-gnu -mno-global-merge -Wno-unused-but-set-variable -Wno-unused-const-variable -fomit-frame-pointer -ftrivial-auto-var-init=pattern -fno-stack-clash-protection -falign-functions=32 -Wdeclaration-after-statement -Wvla -Wno-pointer-sign -Wno-array-bounds -fno-strict-overflow -fno-stack-check -Werror=date-time -Werror=incompatible-pointer-types -Wno-initializer-overrides -Wno-format -Wno-sign-compare -Wno-format-zero-length -Wno-pointer-to-enum-cast -Wno-tautological-constant-out-of-range-compare -DKBUILD_MODFILE='"drivers/mfd/si476x-core"' -DKBUILD_BASENAME='"si476x_cmd"' -DKBUILD_MODNAME='"si476x_core"' -D__KBUILD_MODNAME=kmod_si476x_core -c -o drivers/mfd/si476x-cmd.o drivers/mfd/si476x-cmd.c
-------------
LLVM cannot compile the following code for x86 32bit target, the reason is tail call(TCRETURNmi) is using 2 registers for index+base and we want to use more than one registers for passing function args and that is impossible.
This fix is similar to 3cf3ffce240e("Fix the TCRETURNmi64 bug differently.").
We will only use tail call when it is using <=1 registers for passing args.
```
struct BIG_PARM {
int ver;
};
static struct {
int (*foo) (struct BIG_PARM* a, void *b);
int (*bar) (struct BIG_PARM* a);
int (*zoo0) (void);
int (*zoo1) (void);
int (*zoo2) (void);
int (*zoo3) (void);
int (*zoo4) (void);
} vtable[] = {
[0] = {
.foo = (int (*)(struct BIG_PARM* a, void *b))0xdeadbeef,
},
};
int something(struct BIG_PARM *a, void* b) {
return vtable[a->ver].foo(a,b);
}
```
```
$ clang -std=gnu89 -m32 -mregparm=3 -mtune=generic -fno-strict-overflow -O2 -c t0.c -o t0.c.o
error: ran out of registers during register allocation
1 error generated.
```
Reviewed By: pengfei
Differential Revision: https://reviews.llvm.org/D118312
Added:
llvm/test/CodeGen/X86/tailcall-3regparm.ll
Modified:
llvm/lib/Target/X86/X86InstrCompiler.td
Removed:
################################################################################
diff --git a/llvm/lib/Target/X86/X86InstrCompiler.td b/llvm/lib/Target/X86/X86InstrCompiler.td
index 7288ce812138c..36f56cfcf1154 100644
--- a/llvm/lib/Target/X86/X86InstrCompiler.td
+++ b/llvm/lib/Target/X86/X86InstrCompiler.td
@@ -1235,6 +1235,22 @@ def X86tcret_6regs : PatFrag<(ops node:$ptr, node:$off),
return true;
}]>;
+def X86tcret_1reg : PatFrag<(ops node:$ptr, node:$off),
+ (X86tcret node:$ptr, node:$off), [{
+ // X86tcret args: (*chain, ptr, imm, regs..., glue)
+ unsigned NumRegs = 1;
+ LoadSDNode* ld = dyn_cast<LoadSDNode>(N->getOperand(1));
+ const SDValue& BasePtr = ld->getBasePtr();
+ if (isa<FrameIndexSDNode>(BasePtr))
+ NumRegs = 3;
+ else if (BasePtr->getNumOperands() && isa<GlobalAddressSDNode>(BasePtr->getOperand(0)))
+ NumRegs = 3;
+ for (unsigned i = 3, e = N->getNumOperands(); i != e; ++i)
+ if (isa<RegisterSDNode>(N->getOperand(i)) && ( NumRegs-- == 0))
+ return false;
+ return true;
+}]>;
+
def : Pat<(X86tcret ptr_rc_tailcall:$dst, timm:$off),
(TCRETURNri ptr_rc_tailcall:$dst, timm:$off)>,
Requires<[Not64BitMode, NotUseIndirectThunkCalls]>;
@@ -1242,7 +1258,8 @@ def : Pat<(X86tcret ptr_rc_tailcall:$dst, timm:$off),
// FIXME: This is disabled for 32-bit PIC mode because the global base
// register which is part of the address mode may be assigned a
// callee-saved register.
-def : Pat<(X86tcret (load addr:$dst), timm:$off),
+// Similar to X86tcret_6regs, here we only have 1 register left
+def : Pat<(X86tcret_1reg (load addr:$dst), timm:$off),
(TCRETURNmi addr:$dst, timm:$off)>,
Requires<[Not64BitMode, IsNotPIC, NotUseIndirectThunkCalls]>;
diff --git a/llvm/test/CodeGen/X86/tailcall-3regparm.ll b/llvm/test/CodeGen/X86/tailcall-3regparm.ll
new file mode 100644
index 0000000000000..2f5b066267d89
--- /dev/null
+++ b/llvm/test/CodeGen/X86/tailcall-3regparm.ll
@@ -0,0 +1,30 @@
+; RUN: llc < %s -mtriple=i686-linux-gnu -mcpu=pentium | FileCheck %s
+
+; Tail call should not make register allocation fail (x86-32)
+
+%struct.anon = type { i32 (%struct.BIG_PARM*, i8*)*, i32 ()*, i32 ()*, i32 ()*, i32 ()*, i32 ()*, i32 ()* }
+%struct.BIG_PARM = type { i32 }
+
+ at vtable = internal unnamed_addr constant [1 x %struct.anon] [%struct.anon { i32 (%struct.BIG_PARM*, i8*)* inttoptr (i32 -559038737 to i32 (%struct.BIG_PARM*, i8*)*), i32 ()* null, i32 ()* null, i32 ()* null, i32 ()* null, i32 ()* null, i32 ()* null }], align 4
+
+; Function Attrs: nounwind uwtable
+define dso_local i32 @something(%struct.BIG_PARM* inreg noundef %a, i8* inreg noundef %b) local_unnamed_addr #0 {
+entry:
+ ; CHECK: movl (%eax), %ecx
+ ; CHECK-NEXT: leal (%ecx,%ecx,8), %esi
+ ; CHECK-NEXT: leal (%esi,%esi,2), %esi
+ ; CHECK-NEXT: movl vtable(%ecx,%esi), %ecx
+ ; CHECK-NEXT: popl %esi
+ ; CHECK: jmpl *%ecx
+ %ver = getelementptr inbounds %struct.BIG_PARM, %struct.BIG_PARM* %a, i32 0, i32 0
+ %0 = load i32, i32* %ver, align 4
+ %foo = getelementptr [1 x %struct.anon], [1 x %struct.anon]* @vtable, i32 0, i32 %0, i32 0
+ %1 = load i32 (%struct.BIG_PARM*, i8*)*, i32 (%struct.BIG_PARM*, i8*)** %foo, align 4
+ %call = tail call i32 %1(%struct.BIG_PARM* inreg noundef %a, i8* inreg noundef %b) #1
+ ret i32 %call
+}
+
+!llvm.module.flags = !{!0}
+
+!0 = !{i32 1, !"NumRegisterParameters", i32 3}
+
More information about the llvm-commits
mailing list