Skip to content

Commit 4bcf973

Browse files
authored
[CIR] Add Support For Library Builtins (#143984)
This patch upstreams support for builtins that map to a standard library function. Examples would be abort() and printf(). It also fixes a minor issue with the errorNYI for all remaining unimplemented builtins using the mlir::Location instead of the clang AST SourceLocation.
1 parent 30b16ec commit 4bcf973

File tree

5 files changed

+125
-2
lines changed

5 files changed

+125
-2
lines changed

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ struct MissingFeatures {
236236
static bool runCleanupsScope() { return false; }
237237
static bool lowerAggregateLoadStore() { return false; }
238238
static bool dataLayoutTypeAllocSize() { return false; }
239+
static bool asmLabelAttr() { return false; }
239240

240241
// Missing types
241242
static bool dataMemberType() { return false; }

clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,18 @@
2020
#include "mlir/Support/LLVM.h"
2121
#include "clang/AST/Expr.h"
2222
#include "clang/AST/GlobalDecl.h"
23+
#include "clang/CIR/MissingFeatures.h"
2324
#include "llvm/Support/ErrorHandling.h"
2425

2526
using namespace clang;
2627
using namespace clang::CIRGen;
28+
using namespace llvm;
29+
30+
static RValue emitLibraryCall(CIRGenFunction &cgf, const FunctionDecl *fd,
31+
const CallExpr *e, mlir::Operation *calleeValue) {
32+
CIRGenCallee callee = CIRGenCallee::forDirect(calleeValue, GlobalDecl(fd));
33+
return cgf.emitCall(e->getCallee()->getType(), callee, e, ReturnValueSlot());
34+
}
2735

2836
RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
2937
const CallExpr *e,
@@ -49,7 +57,34 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
4957
}
5058
}
5159

52-
mlir::Location loc = getLoc(e->getExprLoc());
53-
cgm.errorNYI(loc, "non constant foldable builtin calls");
60+
const FunctionDecl *fd = gd.getDecl()->getAsFunction();
61+
62+
// If this is an alias for a lib function (e.g. __builtin_sin), emit
63+
// the call using the normal call path, but using the unmangled
64+
// version of the function name.
65+
if (getContext().BuiltinInfo.isLibFunction(builtinID))
66+
return emitLibraryCall(*this, fd, e,
67+
cgm.getBuiltinLibFunction(fd, builtinID));
68+
69+
cgm.errorNYI(e->getSourceRange(), "unimplemented builtin call");
5470
return getUndefRValue(e->getType());
5571
}
72+
73+
/// Given a builtin id for a function like "__builtin_fabsf", return a Function*
74+
/// for "fabsf".
75+
cir::FuncOp CIRGenModule::getBuiltinLibFunction(const FunctionDecl *fd,
76+
unsigned builtinID) {
77+
assert(astContext.BuiltinInfo.isLibFunction(builtinID));
78+
79+
// Get the name, skip over the __builtin_ prefix (if necessary). We may have
80+
// to build this up so provide a small stack buffer to handle the vast
81+
// majority of names.
82+
llvm::SmallString<64> name;
83+
84+
assert(!cir::MissingFeatures::asmLabelAttr());
85+
name = astContext.BuiltinInfo.getName(builtinID).substr(10);
86+
87+
GlobalDecl d(fd);
88+
mlir::Type type = convertType(fd->getType());
89+
return getOrCreateCIRFunction(name, type, d, /*forVTable=*/false);
90+
}

clang/lib/CIR/CodeGen/CIRGenModule.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,10 @@ class CIRGenModule : public CIRGenTypeCache {
301301
cir::FuncType funcType,
302302
const clang::FunctionDecl *funcDecl);
303303

304+
/// Given a builtin id for a function like "__builtin_fabsf", return a
305+
/// Function* for "fabsf".
306+
cir::FuncOp getBuiltinLibFunction(const FunctionDecl *fd, unsigned builtinID);
307+
304308
mlir::IntegerAttr getSize(CharUnits size) {
305309
return builder.getSizeFromCharUnits(size);
306310
}

clang/test/CIR/CodeGen/builtin_call.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,21 @@ float constant_fp_builtin_single() {
7676
// OGCG: define {{.*}}float @_Z26constant_fp_builtin_singlev()
7777
// OGCG: ret float 0x3FB99999A0000000
7878
// OGCG: }
79+
80+
void library_builtins() {
81+
__builtin_printf(nullptr);
82+
__builtin_abort();
83+
}
84+
85+
// CIR: cir.func @_Z16library_builtinsv() {
86+
// CIR: %[[NULL:.+]] = cir.const #cir.ptr<null> : !cir.ptr<!s8i>
87+
// CIR: cir.call @printf(%[[NULL]]) : (!cir.ptr<!s8i>) -> !s32i
88+
// CIR: cir.call @abort() : () -> ()
89+
90+
// LLVM: define void @_Z16library_builtinsv()
91+
// LLVM: call i32 (ptr, ...) @printf(ptr null)
92+
// LLVM: call void @abort()
93+
94+
// OGCG: define dso_local void @_Z16library_builtinsv()
95+
// OGCG: call i32 (ptr, ...) @printf(ptr noundef null)
96+
// OGCG: call void @abort()
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-cir %s -o %t.cir
2+
// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
3+
// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-llvm %s -o %t-cir.ll
4+
// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM
5+
// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-linux-gnu -Wno-unused-value -emit-llvm %s -o %t.ll
6+
// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
7+
8+
// CIR: cir.global "private" cir_private dsolocal @".str" = #cir.const_array<"%s\00" : !cir.array<!s8i x 3>> : !cir.array<!s8i x 3>
9+
// CIR: cir.global "private" cir_private dsolocal @".str.1" = #cir.const_array<"%s %d\0A\00" : !cir.array<!s8i x 7>> : !cir.array<!s8i x 7>
10+
// LLVM: @.str = private global [3 x i8] c"%s\00"
11+
// LLVM: @.str.1 = private global [7 x i8] c"%s %d\0A\00"
12+
// OGCG: @.str = private unnamed_addr constant [3 x i8] c"%s\00"
13+
// OGCG: @.str.1 = private unnamed_addr constant [7 x i8] c"%s %d\0A\00"
14+
15+
void func(char const * const str, int i) {
16+
__builtin_printf(nullptr);
17+
__builtin_printf("%s", str);
18+
__builtin_printf("%s %d\n", str, i);
19+
}
20+
21+
// CIR: cir.func @printf(!cir.ptr<!s8i>, ...) -> !s32i
22+
23+
// CIR: cir.func @_Z4funcPKci(%[[arg0:.+]]: !cir.ptr<!s8i>{{.*}}, %[[arg1:.+]]: !s32i{{.*}}) {
24+
// CIR: %[[str_ptr:.+]] = cir.alloca !cir.ptr<!s8i>, !cir.ptr<!cir.ptr<!s8i>>, ["str", init, const]
25+
// CIR: %[[i_ptr:.+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["i", init]
26+
// CIR: cir.store %[[arg0]], %[[str_ptr]] : !cir.ptr<!s8i>, !cir.ptr<!cir.ptr<!s8i>>
27+
// CIR: cir.store %[[arg1]], %[[i_ptr]] : !s32i, !cir.ptr<!s32i>
28+
// CIR: %[[null_ptr:.+]] = cir.const #cir.ptr<null> : !cir.ptr<!s8i>
29+
// CIR: %[[printf_result1:.+]] = cir.call @printf(%[[null_ptr]]) : (!cir.ptr<!s8i>) -> !s32i
30+
// CIR: %[[str_fmt_global:.+]] = cir.get_global @".str" : !cir.ptr<!cir.array<!s8i x 3>>
31+
// CIR: %[[str_fmt_ptr:.+]] = cir.cast(array_to_ptrdecay, %[[str_fmt_global]] : !cir.ptr<!cir.array<!s8i x 3>>), !cir.ptr<!s8i>
32+
// CIR: %[[str_val:.+]] = cir.load{{.*}} %[[str_ptr]] : !cir.ptr<!cir.ptr<!s8i>>, !cir.ptr<!s8i>
33+
// CIR: %[[printf_result2:.+]] = cir.call @printf(%[[str_fmt_ptr]], %[[str_val]]) : (!cir.ptr<!s8i>, !cir.ptr<!s8i>) -> !s32i
34+
// CIR: %[[full_fmt_global:.+]] = cir.get_global @".str.1" : !cir.ptr<!cir.array<!s8i x 7>>
35+
// CIR: %[[full_fmt_ptr:.+]] = cir.cast(array_to_ptrdecay, %[[full_fmt_global]] : !cir.ptr<!cir.array<!s8i x 7>>), !cir.ptr<!s8i>
36+
// CIR: %[[str_val2:.+]] = cir.load{{.*}} %[[str_ptr]] : !cir.ptr<!cir.ptr<!s8i>>, !cir.ptr<!s8i>
37+
// CIR: %[[i_val:.+]] = cir.load{{.*}} %[[i_ptr]] : !cir.ptr<!s32i>, !s32i
38+
// CIR: %[[printf_result3:.+]] = cir.call @printf(%[[full_fmt_ptr]], %[[str_val2]], %[[i_val]]) : (!cir.ptr<!s8i>, !cir.ptr<!s8i>, !s32i) -> !s32i
39+
// CIR: cir.return
40+
41+
// LLVM: define void @_Z4funcPKci(ptr %[[arg0:.+]], i32 %[[arg1:.+]])
42+
// LLVM: %[[str_ptr:.+]] = alloca ptr
43+
// LLVM: %[[i_ptr:.+]] = alloca i32
44+
// LLVM: store ptr %[[arg0]], ptr %[[str_ptr]]{{.*}}
45+
// LLVM: store i32 %[[arg1]], ptr %[[i_ptr]]{{.*}}
46+
// LLVM: %[[printf_result1:.+]] = call i32 (ptr, ...) @printf(ptr null)
47+
// LLVM: %[[str_val:.+]] = load ptr, ptr %[[str_ptr]]{{.*}}
48+
// LLVM: %[[printf_result2:.+]] = call i32 (ptr, ...) @printf(ptr @.str, ptr %[[str_val]])
49+
// LLVM: %[[str_val2:.+]] = load ptr, ptr %[[str_ptr]]{{.*}}
50+
// LLVM: %[[i_val:.+]] = load i32, ptr %[[i_ptr]]{{.*}}
51+
// LLVM: %[[printf_result3:.+]] = call i32 (ptr, ...) @printf(ptr @.str.1, ptr %[[str_val2]], i32 %[[i_val]])
52+
// LLVM: ret void
53+
54+
// OGCG: define dso_local void @_Z4funcPKci(ptr noundef %[[arg0:.+]], i32 noundef %[[arg1:.+]])
55+
// OGCG: %[[str_ptr:.+]] = alloca ptr
56+
// OGCG: %[[i_ptr:.+]] = alloca i32
57+
// OGCG: store ptr %[[arg0]], ptr %[[str_ptr]]{{.*}}
58+
// OGCG: store i32 %[[arg1]], ptr %[[i_ptr]]{{.*}}
59+
// OGCG: %[[printf_result1:.+]] = call i32 (ptr, ...) @printf(ptr noundef null)
60+
// OGCG: %[[str_val:.+]] = load ptr, ptr %[[str_ptr]]{{.*}}
61+
// OGCG: %[[printf_result2:.+]] = call i32 (ptr, ...) @printf(ptr noundef @.str, ptr noundef %[[str_val]])
62+
// OGCG: %[[str_val2:.+]] = load ptr, ptr %[[str_ptr]]{{.*}}
63+
// OGCG: %[[i_val:.+]] = load i32, ptr %[[i_ptr]]{{.*}}
64+
// OGCG: %[[printf_result3:.+]] = call i32 (ptr, ...) @printf(ptr noundef @.str.1, ptr noundef %[[str_val2]], i32 noundef %[[i_val]])
65+
// OGCG: ret void

0 commit comments

Comments
 (0)