[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