Skip to content

Commit c167c0a

Browse files
[BuildLibCalls] infer inreg param attrs from NumRegisterParameters
We're having a hard time booting the ARCH=i386 Linux kernel with clang after removing -ffreestanding because instcombine was dropping inreg from callers during libcall simplification, but not the callees defined in different translation units. This led the callers and callees to have wildly different calling conventions, which (predictably) blew up at runtime. Infer the inreg param attrs on function declarations from the module metadata "NumRegisterParameters." This allows us to boot the ARCH=i386 Linux kernel (w/ -ffreestanding removed). Fixes: #53645 Reviewed By: efriedma Differential Revision: https://reviews.llvm.org/D125285
1 parent 6baaad7 commit c167c0a

File tree

2 files changed

+97
-0
lines changed

2 files changed

+97
-0
lines changed

llvm/lib/Transforms/Utils/BuildLibCalls.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,15 @@
1515
#include "llvm/ADT/Statistic.h"
1616
#include "llvm/Analysis/MemoryBuiltins.h"
1717
#include "llvm/Analysis/TargetLibraryInfo.h"
18+
#include "llvm/IR/Argument.h"
19+
#include "llvm/IR/CallingConv.h"
1820
#include "llvm/IR/Constants.h"
1921
#include "llvm/IR/DataLayout.h"
2022
#include "llvm/IR/Function.h"
2123
#include "llvm/IR/IRBuilder.h"
2224
#include "llvm/IR/Module.h"
2325
#include "llvm/IR/Type.h"
26+
#include "llvm/Support/TypeSize.h"
2427

2528
using namespace llvm;
2629

@@ -1224,6 +1227,41 @@ static void setArgExtAttr(Function &F, unsigned ArgNo,
12241227
F.addParamAttr(ArgNo, ExtAttr);
12251228
}
12261229

1230+
// Modeled after X86TargetLowering::markLibCallAttributes.
1231+
static void markRegisterParameterAttributes(Function *F) {
1232+
if (!F->arg_size() || F->isVarArg())
1233+
return;
1234+
1235+
const CallingConv::ID CC = F->getCallingConv();
1236+
if (CC != CallingConv::C && CC != CallingConv::X86_StdCall)
1237+
return;
1238+
1239+
const Module *M = F->getParent();
1240+
unsigned N = M->getNumberRegisterParameters();
1241+
if (!N)
1242+
return;
1243+
1244+
const DataLayout &DL = M->getDataLayout();
1245+
1246+
for (Argument &A : F->args()) {
1247+
Type *T = A.getType();
1248+
if (!T->isIntOrPtrTy())
1249+
continue;
1250+
1251+
const TypeSize &TS = DL.getTypeAllocSize(T);
1252+
if (TS > 8)
1253+
continue;
1254+
1255+
assert(TS <= 4 && "Need to account for parameters larger than word size");
1256+
const unsigned NumRegs = TS > 4 ? 2 : 1;
1257+
if (N < NumRegs)
1258+
return;
1259+
1260+
N -= NumRegs;
1261+
F->addParamAttr(A.getArgNo(), Attribute::InReg);
1262+
}
1263+
}
1264+
12271265
FunctionCallee llvm::getOrInsertLibFunc(Module *M, const TargetLibraryInfo &TLI,
12281266
LibFunc TheLibFunc, FunctionType *T,
12291267
AttributeList AttributeList) {
@@ -1289,6 +1327,8 @@ FunctionCallee llvm::getOrInsertLibFunc(Module *M, const TargetLibraryInfo &TLI,
12891327
break;
12901328
}
12911329

1330+
markRegisterParameterAttributes(F);
1331+
12921332
return C;
12931333
}
12941334

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
; RUN: opt -passes=instcombine -S %s | FileCheck %s
2+
3+
; The intent of this test is to check that the declarations produces for
4+
; libcalls retains the inreg parameter attribute.
5+
6+
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
7+
target triple = "i386-unknown-linux-gnu"
8+
9+
declare ptr @foo()
10+
declare i32 @memcmp(ptr inreg nocapture noundef, ptr inreg nocapture noundef, i32 inreg noundef)
11+
declare i32 @printf(i8*, ...)
12+
declare double @exp2(double)
13+
declare i32 @__sprintf_chk(i8*, i32, i32, i8*, ...)
14+
@a = common global [60 x i8] zeroinitializer, align 1
15+
@b = common global [60 x i8] zeroinitializer, align 1
16+
@h = constant [2 x i8] c"h\00"
17+
18+
; CHECK: declare i32 @bcmp(ptr inreg nocapture, ptr inreg nocapture, i32 inreg)
19+
; CHECK-NOT: declare i32 @bcmp(ptr nocapture, ptr nocapture, i32)
20+
21+
define i32 @baz(ptr inreg noundef %s2, i32 inreg noundef %n){
22+
%call = call ptr @foo()
23+
%call1 = call i32 @memcmp(ptr inreg noundef %call, ptr inreg noundef %s2, i32 inreg noundef %n)
24+
%cmp = icmp eq i32 %call1, 0
25+
%conv = zext i1 %cmp to i32
26+
ret i32 %conv
27+
}
28+
29+
; CHECK: declare noundef i32 @putchar(i32 inreg noundef)
30+
; CHECK-NOT: declare noundef i32 @putchar(i32 noundef)
31+
32+
define void @test_fewer_params_than_num_register_parameters() {
33+
%fmt = getelementptr [2 x i8], [2 x i8]* @h, i32 0, i32 0
34+
call i32 (i8*, ...) @printf(i8* %fmt)
35+
ret void
36+
}
37+
38+
; CHECK: declare double @ldexp(double, i32 inreg)
39+
; CHECK-NOT: declare double @ldexp(double, i32)
40+
41+
define double @test_non_int_params(i16 signext %x) {
42+
%conv = sitofp i16 %x to double
43+
%ret = call double @exp2(double %conv)
44+
ret double %ret
45+
}
46+
47+
; CHECK: declare noundef i32 @sprintf(ptr noalias nocapture noundef writeonly, ptr nocapture noundef readonly, ...)
48+
; CHECK-NOT: declare noundef i32 @sprintf(ptr inreg noalias nocapture noundef writeonly, ptr inreg nocapture noundef readonly, ...)
49+
define i32 @test_variadic() {
50+
%dst = getelementptr inbounds [60 x i8], [60 x i8]* @a, i32 0, i32 0
51+
%fmt = getelementptr inbounds [60 x i8], [60 x i8]* @b, i32 0, i32 0
52+
%ret = call i32 (i8*, i32, i32, i8*, ...) @__sprintf_chk(i8* %dst, i32 0, i32 -1, i8* %fmt)
53+
ret i32 %ret
54+
}
55+
56+
!llvm.module.flags = !{!0}
57+
!0 = !{i32 1, !"NumRegisterParameters", i32 3}

0 commit comments

Comments
 (0)