Skip to content

Commit 63b1a20

Browse files
committed
[CIR] Add support for SourceLocExpr (__builtin_LINE, etc.)
This patch implements SourceLocExpr code generation, which enables support for the following builtin functions: - __builtin_LINE - __builtin_FILE - __builtin_FUNCTION - __builtin_COLUMN These builtins are evaluated at compile-time and emit constant values or global string literals. The implementation follows the traditional CodeGen approach of using ConstantEmitter to emit abstract constants from evaluated APValues. A key challenge was handling synthetic StringLiterals created during constant evaluation (for __builtin_FILE and __builtin_FUNCTION). These synthetic literals don't have valid source locations, which caused assertion failures when creating global string constants. The fix adds a check in getGlobalForStringLiteral to use UnknownLoc when the StringLiteral has an invalid source range. This feature unblocks: - std::source_location (C++20) - Logging and debugging macros that use source location builtins - Assertion/diagnostic frameworks Test coverage includes: - Basic __builtin_LINE, __builtin_FILE, __builtin_FUNCTION, __builtin_COLUMN - Usage in global variable initializers - Default function arguments - Lambda expressions - Combined usage of multiple source location builtins ghstack-source-id: 6d3f35e Pull-Request: #1988
1 parent ca31760 commit 63b1a20

File tree

3 files changed

+89
-2
lines changed

3 files changed

+89
-2
lines changed

clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "Address.h"
14+
#include "CIRGenCstEmitter.h"
1415
#include "CIRGenFunction.h"
1516
#include "CIRGenModule.h"
1617
#include "CIRGenOpenMPRuntime.h"
@@ -705,7 +706,17 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
705706
mlir::Value VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *E) {
706707
return emitLoadOfLValue(E);
707708
}
708-
mlir::Value VisitSourceLocExpr(SourceLocExpr *E) { llvm_unreachable("NYI"); }
709+
mlir::Value VisitSourceLocExpr(SourceLocExpr *E) {
710+
auto &Ctx = CGF.getContext();
711+
APValue Evaluated =
712+
E->EvaluateInContext(Ctx, CGF.CurSourceLocExprScope.getDefaultExpr());
713+
auto C = ConstantEmitter(CGF).emitAbstract(E->getLocation(), Evaluated,
714+
E->getType());
715+
mlir::TypedAttr typedAttr = mlir::dyn_cast_or_null<mlir::TypedAttr>(C);
716+
assert(typedAttr && "SourceLocExpr must produce a typed constant");
717+
return cir::ConstantOp::create(Builder, CGF.getLoc(E->getExprLoc()),
718+
typedAttr);
719+
}
709720
mlir::Value VisitCXXDefaultArgExpr(CXXDefaultArgExpr *DAE) {
710721
CIRGenFunction::CXXDefaultArgExprScope Scope(CGF, DAE);
711722
return Visit(DAE->getExpr());

clang/lib/CIR/CodeGen/CIRGenModule.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1855,7 +1855,10 @@ cir::GlobalOp CIRGenModule::getGlobalForStringLiteral(const StringLiteral *s,
18551855
// Unlike LLVM IR, CIR doesn't automatically unique names for globals, so
18561856
// we need to do that explicitly.
18571857
std::string uniqueName = getUniqueGlobalName(globalVariableName.str());
1858-
auto loc = getLoc(s->getSourceRange());
1858+
// Synthetic string literals (e.g., from SourceLocExpr) may not have valid
1859+
// source locations. Use unknown location in those cases.
1860+
auto loc = s->getBeginLoc().isValid() ? getLoc(s->getSourceRange())
1861+
: builder.getUnknownLoc();
18591862
auto typedC = llvm::dyn_cast<mlir::TypedAttr>(c);
18601863
if (!typedC)
18611864
llvm_unreachable("this should never be untyped at this point");
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -fclangir -emit-cir %s -o %t.cir
2+
// RUN: FileCheck --input-file=%t.cir %s
3+
4+
// Test __builtin_LINE
5+
int test_builtin_LINE() {
6+
// CHECK-LABEL: cir.func {{.*}}@{{.*}}test_builtin_LINE
7+
// CHECK: %{{.*}} = cir.const #cir.int<8> : !u32i
8+
return __builtin_LINE();
9+
}
10+
11+
// Test __builtin_FILE
12+
const char* test_builtin_FILE() {
13+
// CHECK-LABEL: cir.func {{.*}}@{{.*}}test_builtin_FILE
14+
// CHECK: %{{.*}} = cir.const #cir.global_view<@".str{{.*}}"> : !cir.ptr<!s8i>
15+
return __builtin_FILE();
16+
}
17+
18+
// Test __builtin_FUNCTION
19+
const char* test_builtin_FUNCTION() {
20+
// CHECK-LABEL: cir.func {{.*}}@{{.*}}test_builtin_FUNCTION
21+
// CHECK: %{{.*}} = cir.const #cir.global_view<@".str{{.*}}"> : !cir.ptr<!s8i>
22+
return __builtin_FUNCTION();
23+
}
24+
25+
// Test __builtin_COLUMN
26+
int test_builtin_COLUMN() {
27+
// CHECK-LABEL: cir.func {{.*}}@{{.*}}test_builtin_COLUMN
28+
// The column number is the position of '__builtin_COLUMN'
29+
// CHECK: %{{.*}} = cir.const #cir.int<10> : !u32i
30+
return __builtin_COLUMN();
31+
}
32+
33+
// Test in global context
34+
#line 100 "test_file.cpp"
35+
int global_line = __builtin_LINE();
36+
// CHECK: cir.global external @global_line = #cir.int<100> : !s32i
37+
38+
// Test default argument
39+
int get_line(int l = __builtin_LINE()) {
40+
return l;
41+
}
42+
43+
void test_default_arg() {
44+
// CHECK-LABEL: cir.func {{.*}}@{{.*}}test_default_arg
45+
// The LINE should be from the call site, not the default argument definition
46+
#line 111
47+
int x = get_line();
48+
// CHECK: %{{.*}} = cir.const #cir.int<111> : !u32i
49+
// CHECK: %{{.*}} = cir.call @{{.*}}get_line{{.*}}({{.*}}) :
50+
}
51+
52+
#line 200 "lambda-test.cpp"
53+
// Test in lambda (this tests that source location correctly captures context)
54+
void test_in_lambda() {
55+
// CHECK-LABEL: cir.func {{.*}}@{{.*}}test_in_lambda
56+
auto lambda = []() {
57+
return __builtin_LINE();
58+
};
59+
int x = lambda();
60+
}
61+
62+
#line 214 "combined-test.cpp"
63+
// Test multiple builtins in one expression
64+
void test_combined() {
65+
// CHECK-LABEL: cir.func {{.*}}@{{.*}}test_combined
66+
const char* file = __builtin_FILE();
67+
int line = __builtin_LINE();
68+
const char* func = __builtin_FUNCTION();
69+
// All should produce constants
70+
// CHECK: cir.const
71+
// CHECK: cir.const
72+
// CHECK: cir.const
73+
}

0 commit comments

Comments
 (0)