yonghong-song updated this revision to Diff 261976.
yonghong-song edited the summary of this revision.
yonghong-song added a comment.
Herald added subscribers: ormris, hiraditya, mgorny.
add second argument to __builtin_btf_type_id() to indicate whether a relocation
should be generated or not.
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D74668/new/
https://reviews.llvm.org/D74668
Files:
clang/include/clang/Basic/BuiltinsBPF.def
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/lib/CodeGen/CGBuiltin.cpp
clang/lib/Sema/SemaChecking.cpp
clang/test/CodeGen/builtin-bpf-btf-type-id.c
clang/test/Sema/builtin-bpf-btf-type-id.c
llvm/include/llvm/IR/IntrinsicsBPF.td
llvm/lib/Target/BPF/BPF.h
llvm/lib/Target/BPF/BPFCORE.h
llvm/lib/Target/BPF/BPFMISimplifyPatchable.cpp
llvm/lib/Target/BPF/BPFPreserveDIType.cpp
llvm/lib/Target/BPF/BPFTargetMachine.cpp
llvm/lib/Target/BPF/BTFDebug.cpp
llvm/lib/Target/BPF/BTFDebug.h
llvm/lib/Target/BPF/CMakeLists.txt
llvm/test/CodeGen/BPF/BTF/builtin-btf-type-id.ll
Index: llvm/test/CodeGen/BPF/BTF/builtin-btf-type-id.ll
===================================================================
--- /dev/null
+++ llvm/test/CodeGen/BPF/BTF/builtin-btf-type-id.ll
@@ -0,0 +1,144 @@
+; RUN: llc -march=bpfel -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s
+; RUN: llc -march=bpfeb -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s
+; RUN: llc -march=bpfel -mattr=+alu32 -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s
+; RUN: llc -march=bpfeb -mattr=+alu32 -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s
+;
+; Source code:
+; static int (*bpf_log)(unsigned tid, void *data, int data_size) = (void *)999;
+; struct {
+; char f1[100];
+; typeof(3) f2;
+; } tmp__abc = {1, 3};
+; void prog1() {
+; bpf_log(__builtin_btf_type_id(tmp__abc, 0), &tmp__abc, sizeof(tmp__abc));
+; }
+; void prog2() {
+; bpf_log(__builtin_btf_type_id(&tmp__abc, 1), &tmp__abc, sizeof(tmp__abc));
+; }
+; void prog3() {
+; bpf_log(__builtin_btf_type_id(tmp__abc.f1[3], 1), &tmp__abc, sizeof(tmp__abc));
+; }
+; Compilation flag:
+; clang -target bpf -O2 -g -S -emit-llvm test.c
+
+%struct.anon = type { [100 x i8], i32 }
+
+@tmp__abc = dso_local global { <{ i8, i8, [98 x i8] }>, i32 } { <{ i8, i8, [98 x i8] }> <{ i8 1, i8 3, [98 x i8] zeroinitializer }>, i32 0 }, align 4, !dbg !0
+
+; Function Attrs: nounwind
+define dso_local void @prog1() local_unnamed_addr #0 !dbg !28 {
+entry:
+ %0 = tail call i32 @llvm.bpf.btf.type.id.p0s_struct.anons.i32(%struct.anon* bitcast ({ <{ i8, i8, [98 x i8] }>, i32 }* @tmp__abc to %struct.anon*), i32 1, i64 0), !dbg !31, !llvm.preserve.access.index !7
+ %call = tail call i32 inttoptr (i64 999 to i32 (i32, i8*, i32)*)(i32 %0, i8* getelementptr inbounds ({ <{ i8, i8, [98 x i8] }>, i32 }, { <{ i8, i8, [98 x i8] }>, i32 }* @tmp__abc, i64 0, i32 0, i32 0), i32 104) #2, !dbg !32
+ ret void, !dbg !33
+}
+
+; Function Attrs: nounwind readnone
+declare i32 @llvm.bpf.btf.type.id.p0s_struct.anons.i32(%struct.anon*, i32, i64) #1
+
+; Function Attrs: nounwind
+define dso_local void @prog2() local_unnamed_addr #0 !dbg !34 {
+entry:
+ %0 = tail call i32 @llvm.bpf.btf.type.id.p0s_struct.anons.i32(%struct.anon* bitcast ({ <{ i8, i8, [98 x i8] }>, i32 }* @tmp__abc to %struct.anon*), i32 0, i64 1), !dbg !35, !llvm.preserve.access.index !6
+ %call = tail call i32 inttoptr (i64 999 to i32 (i32, i8*, i32)*)(i32 %0, i8* getelementptr inbounds ({ <{ i8, i8, [98 x i8] }>, i32 }, { <{ i8, i8, [98 x i8] }>, i32 }* @tmp__abc, i64 0, i32 0, i32 0), i32 104) #2, !dbg !36
+ ret void, !dbg !37
+}
+
+; Function Attrs: nounwind
+define dso_local void @prog3() local_unnamed_addr #0 !dbg !38 {
+entry:
+ %0 = tail call i32 @llvm.bpf.btf.type.id.p0i8.i32(i8* getelementptr inbounds ({ <{ i8, i8, [98 x i8] }>, i32 }, { <{ i8, i8, [98 x i8] }>, i32 }* @tmp__abc, i64 0, i32 0, i32 2, i64 1), i32 1, i64 1), !dbg !39, !llvm.preserve.access.index !11
+ %call = tail call i32 inttoptr (i64 999 to i32 (i32, i8*, i32)*)(i32 %0, i8* getelementptr inbounds ({ <{ i8, i8, [98 x i8] }>, i32 }, { <{ i8, i8, [98 x i8] }>, i32 }* @tmp__abc, i64 0, i32 0, i32 0), i32 104) #2, !dbg !40
+ ret void, !dbg !41
+}
+
+; CHECK-LABEL: prog1
+; CHECK: r1 = 3
+; CHECK-LABEL: prog2
+; CHECK: r1 = 10
+; CHECK-LABEL: prog3
+; CHECK: r1 = 4
+;
+; CHECK: .long 0 # BTF_KIND_STRUCT(id = 3)
+; CHECK-NEXT: .long 67108866 # 0x4000002
+; CHECK-NEXT: .long 104
+; CHECK-NEXT: .long 13
+; CHECK-NEXT: .long 5
+; CHECK-NEXT: .long 0 # 0x0
+; CHECK-NEXT: .long 16
+; CHECK-NEXT: .long 7
+; CHECK-NEXT: .long 800 # 0x320
+; CHECK-NEXT: .long 19 # BTF_KIND_INT(id = 4)
+; CHECK-NEXT: .long 16777216 # 0x1000000
+; CHECK-NEXT: .long 1
+; CHECK-NEXT: .long 16777224 # 0x1000008
+; CHECK: .long 0 # BTF_KIND_PTR(id = 10)
+; CHECK-NEXT: .long 33554432 # 0x2000000
+; CHECK-NEXT: .long 3
+
+; CHECK: .long 16 # FieldReloc
+; CHECK-NEXT: .long {{[0-9]+}} # Field reloc section string offset={{[0-9]+}}
+; CHECK-NEXT: .long 2
+; CHECK-NEXT: .long .Ltmp{{[0-9]+}}
+; CHECK-NEXT: .long 10
+; CHECK-NEXT: .long {{[0-9]+}}
+; CHECK-NEXT: .long 6
+; CHECK-NEXT: .long .Ltmp{{[0-9]+}}
+; CHECK-NEXT: .long 4
+; CHECK-NEXT: .long {{[0-9]+}}
+; CHECK-NEXT: .long 6
+
+
+; Function Attrs: nounwind readnone
+declare i32 @llvm.bpf.btf.type.id.p0i8.i32(i8*, i32, i64) #1
+
+attributes #0 = { nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #1 = { nounwind readnone }
+attributes #2 = { nounwind }
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!24, !25, !26}
+!llvm.ident = !{!27}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "tmp__abc", scope: !2, file: !3, line: 5, type: !7, isLocal: false, isDefinition: true)
+!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version 11.0.0 (https://github.com/llvm/llvm-project.git 95253d8f16b8085b4b85cb3a6106ccbfe8a6d9b2)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, retainedTypes: !5, globals: !16, splitDebugInlining: false, nameTableKind: None)
+!3 = !DIFile(filename: "test.c", directory: "/tmp/home/yhs/work/tests/core")
+!4 = !{}
+!5 = !{!6, !11}
+!6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64)
+!7 = distinct !DICompositeType(tag: DW_TAG_structure_type, file: !3, line: 2, size: 832, elements: !8)
+!8 = !{!9, !14}
+!9 = !DIDerivedType(tag: DW_TAG_member, name: "f1", scope: !7, file: !3, line: 3, baseType: !10, size: 800)
+!10 = !DICompositeType(tag: DW_TAG_array_type, baseType: !11, size: 800, elements: !12)
+!11 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char)
+!12 = !{!13}
+!13 = !DISubrange(count: 100)
+!14 = !DIDerivedType(tag: DW_TAG_member, name: "f2", scope: !7, file: !3, line: 4, baseType: !15, size: 32, offset: 800)
+!15 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!16 = !{!0, !17}
+!17 = !DIGlobalVariableExpression(var: !18, expr: !DIExpression())
+!18 = distinct !DIGlobalVariable(name: "bpf_log", scope: !2, file: !3, line: 1, type: !19, isLocal: true, isDefinition: true)
+!19 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !20, size: 64)
+!20 = !DISubroutineType(types: !21)
+!21 = !{!15, !22, !23, !15}
+!22 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned)
+!23 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64)
+!24 = !{i32 7, !"Dwarf Version", i32 4}
+!25 = !{i32 2, !"Debug Info Version", i32 3}
+!26 = !{i32 1, !"wchar_size", i32 4}
+!27 = !{!"clang version 11.0.0 (https://github.com/llvm/llvm-project.git 95253d8f16b8085b4b85cb3a6106ccbfe8a6d9b2)"}
+!28 = distinct !DISubprogram(name: "prog1", scope: !3, file: !3, line: 6, type: !29, scopeLine: 6, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !4)
+!29 = !DISubroutineType(types: !30)
+!30 = !{null}
+!31 = !DILocation(line: 7, column: 11, scope: !28)
+!32 = !DILocation(line: 7, column: 3, scope: !28)
+!33 = !DILocation(line: 8, column: 1, scope: !28)
+!34 = distinct !DISubprogram(name: "prog2", scope: !3, file: !3, line: 9, type: !29, scopeLine: 9, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !4)
+!35 = !DILocation(line: 10, column: 11, scope: !34)
+!36 = !DILocation(line: 10, column: 3, scope: !34)
+!37 = !DILocation(line: 11, column: 1, scope: !34)
+!38 = distinct !DISubprogram(name: "prog3", scope: !3, file: !3, line: 12, type: !29, scopeLine: 12, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !4)
+!39 = !DILocation(line: 13, column: 11, scope: !38)
+!40 = !DILocation(line: 13, column: 3, scope: !38)
+!41 = !DILocation(line: 14, column: 1, scope: !38)
Index: llvm/lib/Target/BPF/CMakeLists.txt
===================================================================
--- llvm/lib/Target/BPF/CMakeLists.txt
+++ llvm/lib/Target/BPF/CMakeLists.txt
@@ -20,6 +20,7 @@
BPFISelDAGToDAG.cpp
BPFISelLowering.cpp
BPFMCInstLower.cpp
+ BPFPreserveDIType.cpp
BPFRegisterInfo.cpp
BPFSelectionDAGInfo.cpp
BPFSubtarget.cpp
Index: llvm/lib/Target/BPF/BTFDebug.h
===================================================================
--- llvm/lib/Target/BPF/BTFDebug.h
+++ llvm/lib/Target/BPF/BTFDebug.h
@@ -26,6 +26,7 @@
class AsmPrinter;
class BTFDebug;
class DIType;
+class GlobalVariable;
class MCStreamer;
class MCSymbol;
class MachineFunction;
@@ -250,7 +251,7 @@
StringMap<std::vector<std::string>> FileContent;
std::map<std::string, std::unique_ptr<BTFKindDataSec>> DataSecEntries;
std::vector<BTFTypeStruct *> StructTypes;
- std::map<std::string, uint32_t> PatchImms;
+ std::map<const GlobalVariable *, uint32_t> PatchImms;
std::map<StringRef, std::pair<bool, std::vector<BTFTypeDerived *>>>
FixupDerivedTypes;
std::set<const Function *>ProtoFunctions;
@@ -300,11 +301,11 @@
void processFuncPrototypes(const Function *);
/// Generate one field relocation record.
- void generateFieldReloc(const MCSymbol *ORSym, DIType *RootTy,
- StringRef AccessPattern);
+ void generatePatchImmReloc(const MCSymbol *ORSym, uint32_t RootId,
+ const GlobalVariable *, bool IsAma);
- /// Populating unprocessed struct type.
- unsigned populateStructType(const DIType *Ty);
+ /// Populating unprocessed type on demand.
+ unsigned populateType(const DIType *Ty);
/// Process relocation instructions.
void processReloc(const MachineOperand &MO);
Index: llvm/lib/Target/BPF/BTFDebug.cpp
===================================================================
--- llvm/lib/Target/BPF/BTFDebug.cpp
+++ llvm/lib/Target/BPF/BTFDebug.cpp
@@ -933,9 +933,9 @@
SecNameOff = 0;
}
-/// On-demand populate struct types as requested from abstract member
-/// accessing.
-unsigned BTFDebug::populateStructType(const DIType *Ty) {
+/// On-demand populate types as requested from abstract member
+/// accessing or preserve debuginfo type.
+unsigned BTFDebug::populateType(const DIType *Ty) {
unsigned Id;
visitTypeEntry(Ty, Id, false, false);
for (const auto &TypeEntry : TypeEntries)
@@ -944,25 +944,39 @@
}
/// Generate a struct member field relocation.
-void BTFDebug::generateFieldReloc(const MCSymbol *ORSym, DIType *RootTy,
- StringRef AccessPattern) {
- unsigned RootId = populateStructType(RootTy);
- size_t FirstDollar = AccessPattern.find_first_of('$');
- size_t FirstColon = AccessPattern.find_first_of(':');
- size_t SecondColon = AccessPattern.find_first_of(':', FirstColon + 1);
- StringRef IndexPattern = AccessPattern.substr(FirstDollar + 1);
- StringRef RelocKindStr = AccessPattern.substr(FirstColon + 1,
- SecondColon - FirstColon);
- StringRef PatchImmStr = AccessPattern.substr(SecondColon + 1,
- FirstDollar - SecondColon);
-
+void BTFDebug::generatePatchImmReloc(const MCSymbol *ORSym, uint32_t RootId,
+ const GlobalVariable *GVar, bool IsAma) {
BTFFieldReloc FieldReloc;
FieldReloc.Label = ORSym;
- FieldReloc.OffsetNameOff = addString(IndexPattern);
FieldReloc.TypeID = RootId;
- FieldReloc.RelocKind = std::stoull(std::string(RelocKindStr));
- PatchImms[AccessPattern.str()] = std::stoul(std::string(PatchImmStr));
- FieldRelocTable[SecNameOff].push_back(FieldReloc);
+
+ StringRef AccessPattern = GVar->getName();
+ size_t FirstDollar = AccessPattern.find_first_of('$');
+ if (IsAma) {
+ size_t FirstColon = AccessPattern.find_first_of(':');
+ size_t SecondColon = AccessPattern.find_first_of(':', FirstColon + 1);
+ StringRef IndexPattern = AccessPattern.substr(FirstDollar + 1);
+ StringRef RelocKindStr = AccessPattern.substr(FirstColon + 1,
+ SecondColon - FirstColon);
+ StringRef PatchImmStr = AccessPattern.substr(SecondColon + 1,
+ FirstDollar - SecondColon);
+
+ FieldReloc.OffsetNameOff = addString(IndexPattern);
+ FieldReloc.RelocKind = std::stoull(std::string(RelocKindStr));
+ FieldRelocTable[SecNameOff].push_back(FieldReloc);
+ PatchImms[GVar] = std::stoul(std::string(PatchImmStr));
+ } else {
+ // get flag
+ StringRef FlagStr = AccessPattern.substr(FirstDollar + 1);
+ uint64_t Flag = std::stoull(std::string(FlagStr));
+
+ if (Flag == BPFCoreSharedInfo::BTF_TYPE_ID_RELOC) {
+ FieldReloc.OffsetNameOff = addString("0");
+ FieldReloc.RelocKind = BPFCoreSharedInfo::BTF_TYPE_ID;
+ FieldRelocTable[SecNameOff].push_back(FieldReloc);
+ }
+ PatchImms[GVar] = RootId;
+ }
}
void BTFDebug::processReloc(const MachineOperand &MO) {
@@ -970,14 +984,20 @@
if (MO.isGlobal()) {
const GlobalValue *GVal = MO.getGlobal();
auto *GVar = dyn_cast<GlobalVariable>(GVal);
- if (GVar && GVar->hasAttribute(BPFCoreSharedInfo::AmaAttr)) {
- MCSymbol *ORSym = OS.getContext().createTempSymbol();
- OS.emitLabel(ORSym);
+ if (!GVar)
+ return;
- MDNode *MDN = GVar->getMetadata(LLVMContext::MD_preserve_access_index);
- DIType *Ty = dyn_cast<DIType>(MDN);
- generateFieldReloc(ORSym, Ty, GVar->getName());
- }
+ if (!GVar->hasAttribute(BPFCoreSharedInfo::AmaAttr) &&
+ !GVar->hasAttribute(BPFCoreSharedInfo::TypeIdAttr))
+ return;
+
+ MCSymbol *ORSym = OS.getContext().createTempSymbol();
+ OS.emitLabel(ORSym);
+
+ MDNode *MDN = GVar->getMetadata(LLVMContext::MD_preserve_access_index);
+ uint32_t RootId = populateType(dyn_cast<DIType>(MDN));
+ generatePatchImmReloc(ORSym, RootId, GVar,
+ GVar->hasAttribute(BPFCoreSharedInfo::AmaAttr));
}
}
@@ -1013,6 +1033,9 @@
// Later, the insn is replaced with "r2 = <offset>"
// where "<offset>" equals to the offset based on current
// type definitions.
+ //
+ // If the insn is "r2 = LD_imm64 @<an TypeIdAttr global>",
+ // The LD_imm64 result will be replaced with a btf type id.
processReloc(MI->getOperand(1));
} else if (MI->getOpcode() == BPF::CORE_MEM ||
MI->getOpcode() == BPF::CORE_ALU32_MEM ||
@@ -1145,9 +1168,15 @@
if (MO.isGlobal()) {
const GlobalValue *GVal = MO.getGlobal();
auto *GVar = dyn_cast<GlobalVariable>(GVal);
- if (GVar && GVar->hasAttribute(BPFCoreSharedInfo::AmaAttr)) {
- // Emit "mov ri, <imm>" for patched immediate.
- uint32_t Imm = PatchImms[GVar->getName().str()];
+ if (GVar) {
+ // Emit "mov ri, <imm>"
+ uint32_t Imm;
+ if (GVar->hasAttribute(BPFCoreSharedInfo::AmaAttr) ||
+ GVar->hasAttribute(BPFCoreSharedInfo::TypeIdAttr))
+ Imm = PatchImms[GVar];
+ else
+ return false;
+
OutMI.setOpcode(BPF::MOV_ri);
OutMI.addOperand(MCOperand::createReg(MI->getOperand(0).getReg()));
OutMI.addOperand(MCOperand::createImm(Imm));
@@ -1162,7 +1191,7 @@
const GlobalValue *GVal = MO.getGlobal();
auto *GVar = dyn_cast<GlobalVariable>(GVal);
if (GVar && GVar->hasAttribute(BPFCoreSharedInfo::AmaAttr)) {
- uint32_t Imm = PatchImms[GVar->getName().str()];
+ uint32_t Imm = PatchImms[GVar];
OutMI.setOpcode(MI->getOperand(1).getImm());
if (MI->getOperand(0).isImm())
OutMI.addOperand(MCOperand::createImm(MI->getOperand(0).getImm()));
Index: llvm/lib/Target/BPF/BPFTargetMachine.cpp
===================================================================
--- llvm/lib/Target/BPF/BPFTargetMachine.cpp
+++ llvm/lib/Target/BPF/BPFTargetMachine.cpp
@@ -35,6 +35,7 @@
PassRegistry &PR = *PassRegistry::getPassRegistry();
initializeBPFAbstractMemberAccessPass(PR);
+ initializeBPFPreserveDITypePass(PR);
initializeBPFMIPeepholePass(PR);
initializeBPFMIPeepholeTruncElimPass(PR);
}
@@ -96,6 +97,7 @@
void BPFPassConfig::addIRPasses() {
addPass(createBPFAbstractMemberAccess(&getBPFTargetMachine()));
+ addPass(createBPFPreserveDIType());
TargetPassConfig::addIRPasses();
}
Index: llvm/lib/Target/BPF/BPFPreserveDIType.cpp
===================================================================
--- /dev/null
+++ llvm/lib/Target/BPF/BPFPreserveDIType.cpp
@@ -0,0 +1,125 @@
+//===----------- BPFPreserveDIType.cpp - Preserve DebugInfo Types ---------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Preserve Debuginfo types encoded in __builtin_bpf_btf_type_id() metadata.
+//
+//===----------------------------------------------------------------------===//
+
+#include "BPF.h"
+#include "BPFCORE.h"
+#include "llvm/IR/DebugInfoMetadata.h"
+#include "llvm/IR/GlobalVariable.h"
+#include "llvm/IR/Instruction.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/Type.h"
+#include "llvm/IR/User.h"
+#include "llvm/IR/Value.h"
+#include "llvm/Pass.h"
+#include "llvm/Transforms/Utils/BasicBlockUtils.h"
+
+#define DEBUG_TYPE "bpf-preserve-di-type"
+
+namespace llvm {
+const std::string BPFCoreSharedInfo::TypeIdAttr = "btf_type_id";
+} // namespace llvm
+
+using namespace llvm;
+
+namespace {
+
+class BPFPreserveDIType final : public ModulePass {
+ StringRef getPassName() const override {
+ return "BPF Preserve DebugInfo Type";
+ }
+
+ bool runOnModule(Module &M) override;
+
+public:
+ static char ID;
+ BPFPreserveDIType() : ModulePass(ID) {}
+
+private:
+ bool doTransformation(Module &M);
+};
+} // End anonymous namespace
+
+char BPFPreserveDIType::ID = 0;
+INITIALIZE_PASS(BPFPreserveDIType, DEBUG_TYPE, "preserve debuginfo type", false,
+ false)
+
+ModulePass *llvm::createBPFPreserveDIType() { return new BPFPreserveDIType(); }
+
+bool BPFPreserveDIType::runOnModule(Module &M) {
+ LLVM_DEBUG(dbgs() << "********** preserve debuginfo type **********\n");
+
+ // Bail out if no debug info.
+ if (M.debug_compile_units().empty())
+ return false;
+
+ return doTransformation(M);
+}
+
+bool BPFPreserveDIType::doTransformation(Module &M) {
+ std::vector<CallInst *> PreserveDITypeCalls;
+
+ for (auto &F : M) {
+ for (auto &BB : F) {
+ for (auto &I : BB) {
+ auto *Call = dyn_cast<CallInst>(&I);
+ if (!Call)
+ continue;
+
+ const auto *GV = dyn_cast<GlobalValue>(Call->getCalledOperand());
+ if (!GV)
+ continue;
+
+ if (GV->getName().startswith("llvm.bpf.btf.type.id")) {
+ if (!Call->getMetadata(LLVMContext::MD_preserve_access_index))
+ report_fatal_error(
+ "Missing metadata for llvm.bpf.btf.type.id intrinsic");
+ PreserveDITypeCalls.push_back(Call);
+ }
+ }
+ }
+ }
+
+ if (PreserveDITypeCalls.empty())
+ return false;
+
+ std::string BaseName = "llvm.btf_type_id.";
+ int Count = 0;
+ for (auto Call : PreserveDITypeCalls) {
+ const ConstantInt *Flag = dyn_cast<ConstantInt>(Call->getArgOperand(2));
+ assert(Flag);
+ uint64_t FlagValue = Flag->getValue().getZExtValue();
+
+ if (FlagValue >= BPFCoreSharedInfo::MAX_BTF_TYPE_ID_FLAG)
+ report_fatal_error("Incorrect flag for llvm.bpf.btf.type.id intrinsic");
+
+ BasicBlock *BB = Call->getParent();
+ IntegerType *VarType = Type::getInt32Ty(BB->getContext());
+ std::string GVName = BaseName + std::to_string(Count) + "$" +
+ std::to_string(FlagValue);
+ GlobalVariable *GV =
+ new GlobalVariable(M, VarType, false, GlobalVariable::ExternalLinkage,
+ NULL, GVName);
+ GV->addAttribute(BPFCoreSharedInfo::TypeIdAttr);
+ MDNode *MD = Call->getMetadata(LLVMContext::MD_preserve_access_index);
+ GV->setMetadata(LLVMContext::MD_preserve_access_index, MD);
+
+ // Load the global variable which represents the type info.
+ auto *LDInst = new LoadInst(Type::getInt32Ty(BB->getContext()), GV);
+ BB->getInstList().insert(Call->getIterator(), LDInst);
+ Call->replaceAllUsesWith(LDInst);
+ Call->eraseFromParent();
+ Count++;
+ }
+
+ return true;
+}
Index: llvm/lib/Target/BPF/BPFMISimplifyPatchable.cpp
===================================================================
--- llvm/lib/Target/BPF/BPFMISimplifyPatchable.cpp
+++ llvm/lib/Target/BPF/BPFMISimplifyPatchable.cpp
@@ -22,6 +22,9 @@
// r1 = <calculated field_info>
// add r3, struct_base_reg, r1
//
+// This pass also removes the intermediate load generated in IR pass for
+// __builtin_btf_type_id() intrinsic.
+//
//===----------------------------------------------------------------------===//
#include "BPF.h"
@@ -55,10 +58,10 @@
bool removeLD(void);
void processCandidate(MachineRegisterInfo *MRI, MachineBasicBlock &MBB,
MachineInstr &MI, Register &SrcReg, Register &DstReg,
- const GlobalValue *GVal);
+ const GlobalValue *GVal, bool IsAma);
void processDstReg(MachineRegisterInfo *MRI, Register &DstReg,
Register &SrcReg, const GlobalValue *GVal,
- bool doSrcRegProp);
+ bool doSrcRegProp, bool IsAma);
void processInst(MachineRegisterInfo *MRI, MachineInstr *Inst,
MachineOperand *RelocOp, const GlobalValue *GVal);
void checkADDrr(MachineRegisterInfo *MRI, MachineOperand *RelocOp,
@@ -155,25 +158,27 @@
void BPFMISimplifyPatchable::processCandidate(MachineRegisterInfo *MRI,
MachineBasicBlock &MBB, MachineInstr &MI, Register &SrcReg,
- Register &DstReg, const GlobalValue *GVal) {
+ Register &DstReg, const GlobalValue *GVal, bool IsAma) {
if (MRI->getRegClass(DstReg) == &BPF::GPR32RegClass) {
- // We can optimize such a pattern:
- // %1:gpr = LD_imm64 @"llvm.s:0:4$0:2"
- // %2:gpr32 = LDW32 %1:gpr, 0
- // %3:gpr = SUBREG_TO_REG 0, %2:gpr32, %subreg.sub_32
- // %4:gpr = ADD_rr %0:gpr, %3:gpr
- // or similar patterns below for non-alu32 case.
- auto Begin = MRI->use_begin(DstReg), End = MRI->use_end();
- decltype(End) NextI;
- for (auto I = Begin; I != End; I = NextI) {
- NextI = std::next(I);
- if (!MRI->getUniqueVRegDef(I->getReg()))
- continue;
-
- unsigned Opcode = I->getParent()->getOpcode();
- if (Opcode == BPF::SUBREG_TO_REG) {
- Register TmpReg = I->getParent()->getOperand(0).getReg();
- processDstReg(MRI, TmpReg, DstReg, GVal, false);
+ if (IsAma) {
+ // We can optimize such a pattern:
+ // %1:gpr = LD_imm64 @"llvm.s:0:4$0:2"
+ // %2:gpr32 = LDW32 %1:gpr, 0
+ // %3:gpr = SUBREG_TO_REG 0, %2:gpr32, %subreg.sub_32
+ // %4:gpr = ADD_rr %0:gpr, %3:gpr
+ // or similar patterns below for non-alu32 case.
+ auto Begin = MRI->use_begin(DstReg), End = MRI->use_end();
+ decltype(End) NextI;
+ for (auto I = Begin; I != End; I = NextI) {
+ NextI = std::next(I);
+ if (!MRI->getUniqueVRegDef(I->getReg()))
+ continue;
+
+ unsigned Opcode = I->getParent()->getOpcode();
+ if (Opcode == BPF::SUBREG_TO_REG) {
+ Register TmpReg = I->getParent()->getOperand(0).getReg();
+ processDstReg(MRI, TmpReg, DstReg, GVal, false, IsAma);
+ }
}
}
@@ -183,12 +188,12 @@
}
// All uses of DstReg replaced by SrcReg
- processDstReg(MRI, DstReg, SrcReg, GVal, true);
+ processDstReg(MRI, DstReg, SrcReg, GVal, true, IsAma);
}
void BPFMISimplifyPatchable::processDstReg(MachineRegisterInfo *MRI,
Register &DstReg, Register &SrcReg, const GlobalValue *GVal,
- bool doSrcRegProp) {
+ bool doSrcRegProp, bool IsAma) {
auto Begin = MRI->use_begin(DstReg), End = MRI->use_end();
decltype(End) NextI;
for (auto I = Begin; I != End; I = NextI) {
@@ -197,7 +202,7 @@
I->setReg(SrcReg);
// The candidate needs to have a unique definition.
- if (MRI->getUniqueVRegDef(I->getReg()))
+ if (IsAma && MRI->getUniqueVRegDef(I->getReg()))
processInst(MRI, I->getParent(), &*I, GVal);
}
}
@@ -269,28 +274,26 @@
if (!DefInst)
continue;
- bool IsCandidate = false;
- const GlobalValue *GVal = nullptr;
- if (DefInst->getOpcode() == BPF::LD_imm64) {
- const MachineOperand &MO = DefInst->getOperand(1);
- if (MO.isGlobal()) {
- GVal = MO.getGlobal();
- auto *GVar = dyn_cast<GlobalVariable>(GVal);
- if (GVar) {
- // Global variables representing structure offset or
- // patchable extern globals.
- if (GVar->hasAttribute(BPFCoreSharedInfo::AmaAttr)) {
- assert(MI.getOperand(2).getImm() == 0);
- IsCandidate = true;
- }
- }
- }
- }
+ if (DefInst->getOpcode() != BPF::LD_imm64)
+ continue;
+
+ const MachineOperand &MO = DefInst->getOperand(1);
+ if (!MO.isGlobal())
+ continue;
+
+ const GlobalValue *GVal = MO.getGlobal();
+ auto *GVar = dyn_cast<GlobalVariable>(GVal);
+ if (!GVar)
+ continue;
- if (!IsCandidate)
+ // Global variables representing structure offset or type id.
+ bool IsAma = false;
+ if (GVar->hasAttribute(BPFCoreSharedInfo::AmaAttr))
+ IsAma = true;
+ else if (!GVar->hasAttribute(BPFCoreSharedInfo::TypeIdAttr))
continue;
- processCandidate(MRI, MBB, MI, SrcReg, DstReg, GVal);
+ processCandidate(MRI, MBB, MI, SrcReg, DstReg, GVal, IsAma);
ToErase = &MI;
Changed = true;
Index: llvm/lib/Target/BPF/BPFCORE.h
===================================================================
--- llvm/lib/Target/BPF/BPFCORE.h
+++ llvm/lib/Target/BPF/BPFCORE.h
@@ -13,18 +13,29 @@
class BPFCoreSharedInfo {
public:
- enum OffsetRelocKind : uint32_t {
+ enum PatchableRelocKind : uint32_t {
FIELD_BYTE_OFFSET = 0,
FIELD_BYTE_SIZE,
FIELD_EXISTENCE,
FIELD_SIGNEDNESS,
FIELD_LSHIFT_U64,
FIELD_RSHIFT_U64,
+ BTF_TYPE_ID,
MAX_FIELD_RELOC_KIND,
};
+
+ enum BTFTypeIdFlag : uint32_t {
+ BTF_TYPE_ID_NO_RELOC = 0,
+ BTF_TYPE_ID_RELOC,
+
+ MAX_BTF_TYPE_ID_FLAG,
+ };
+
/// The attribute attached to globals representing a field access
static const std::string AmaAttr;
+ /// The attribute attached to globals representing a type id
+ static const std::string TypeIdAttr;
};
} // namespace llvm
Index: llvm/lib/Target/BPF/BPF.h
===================================================================
--- llvm/lib/Target/BPF/BPF.h
+++ llvm/lib/Target/BPF/BPF.h
@@ -16,6 +16,7 @@
class BPFTargetMachine;
ModulePass *createBPFAbstractMemberAccess(BPFTargetMachine *TM);
+ModulePass *createBPFPreserveDIType();
FunctionPass *createBPFISelDag(BPFTargetMachine &TM);
FunctionPass *createBPFMISimplifyPatchablePass();
@@ -25,6 +26,7 @@
FunctionPass *createBPFMIPreEmitCheckingPass();
void initializeBPFAbstractMemberAccessPass(PassRegistry&);
+void initializeBPFPreserveDITypePass(PassRegistry&);
void initializeBPFMISimplifyPatchablePass(PassRegistry&);
void initializeBPFMIPeepholePass(PassRegistry&);
void initializeBPFMIPeepholeTruncElimPass(PassRegistry&);
Index: llvm/include/llvm/IR/IntrinsicsBPF.td
===================================================================
--- llvm/include/llvm/IR/IntrinsicsBPF.td
+++ llvm/include/llvm/IR/IntrinsicsBPF.td
@@ -23,4 +23,7 @@
def int_bpf_preserve_field_info : GCCBuiltin<"__builtin_bpf_preserve_field_info">,
Intrinsic<[llvm_i32_ty], [llvm_anyptr_ty, llvm_i64_ty],
[IntrNoMem, ImmArg<1>]>;
+ def int_bpf_btf_type_id : GCCBuiltin<"__builtin_bpf_btf_type_id">,
+ Intrinsic<[llvm_i32_ty], [llvm_any_ty, llvm_any_ty, llvm_i64_ty],
+ [IntrNoMem]>;
}
Index: clang/test/Sema/builtin-bpf-btf-type-id.c
===================================================================
--- /dev/null
+++ clang/test/Sema/builtin-bpf-btf-type-id.c
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 -x c -triple bpf-pc-linux-gnu -dwarf-version=4 -fsyntax-only -verify %s
+
+struct {
+ char f1[100];
+ int f2;
+} tmp = {};
+
+unsigned invalid1() { return __builtin_btf_type_id(1, tmp); } // expected-error {{__builtin_btf_type_id argument 2 not a constant}}
+unsigned invalid2() { return __builtin_btf_type_id(1, 1, 1); } // expected-error {{too many arguments to function call, expected 2, have 3}}
+
+int valid1() { return __builtin_btf_type_id(tmp, 0); }
+int valid2() { return __builtin_btf_type_id(&tmp, 1); }
+int valid3() { return __builtin_btf_type_id(tmp.f1[4], 10); }
Index: clang/test/CodeGen/builtin-bpf-btf-type-id.c
===================================================================
--- /dev/null
+++ clang/test/CodeGen/builtin-bpf-btf-type-id.c
@@ -0,0 +1,13 @@
+// REQUIRES: bpf-registered-target
+// RUN: %clang -target bpf -emit-llvm -S -g %s -o - | FileCheck %s
+
+unsigned test1(int a) { return __builtin_btf_type_id(a, 0); }
+unsigned test2(int a) { return __builtin_btf_type_id(&a, 0); }
+
+// CHECK: define dso_local i32 @test1
+// CHECK: call i32 @llvm.bpf.btf.type.id.p0i32.i32(i32* %{{[0-9a-z.]+}}, i32 1, i64 0), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[INT:[0-9]+]]
+// CHECK: define dso_local i32 @test2
+// CHECK: call i32 @llvm.bpf.btf.type.id.p0i32.i32(i32* %{{[0-9a-z.]+}}, i32 0, i64 0), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[INT_POINTER:[0-9]+]]
+//
+// CHECK: ![[INT]] = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed
+// CHECK: ![[INT_POINTER]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[INT]], size: 64
Index: clang/lib/Sema/SemaChecking.cpp
===================================================================
--- clang/lib/Sema/SemaChecking.cpp
+++ clang/lib/Sema/SemaChecking.cpp
@@ -2484,17 +2484,33 @@
bool Sema::CheckBPFBuiltinFunctionCall(unsigned BuiltinID,
CallExpr *TheCall) {
- assert(BuiltinID == BPF::BI__builtin_preserve_field_info &&
+ assert((BuiltinID == BPF::BI__builtin_preserve_field_info ||
+ BuiltinID == BPF::BI__builtin_btf_type_id) &&
"unexpected ARM builtin");
if (checkArgCount(*this, TheCall, 2))
return true;
+ Expr *Arg;
+ if (BuiltinID == BPF::BI__builtin_btf_type_id) {
+ // The second argument needs to be a constant int
+ llvm::APSInt Value;
+ Arg = TheCall->getArg(1);
+ if (!Arg->isIntegerConstantExpr(Value, Context)) {
+ Diag(Arg->getBeginLoc(), diag::err_btf_type_id_not_const)
+ << 2 << Arg->getSourceRange();
+ return true;
+ }
+
+ TheCall->setType(Context.UnsignedIntTy);
+ return false;
+ }
+
// The first argument needs to be a record field access.
// If it is an array element access, we delay decision
// to BPF backend to check whether the access is a
// field access or not.
- Expr *Arg = TheCall->getArg(0);
+ Arg = TheCall->getArg(0);
if (Arg->getType()->getAsPlaceholderType() ||
(Arg->IgnoreParens()->getObjectKind() != OK_BitField &&
!dyn_cast<MemberExpr>(Arg->IgnoreParens()) &&
@@ -2505,8 +2521,9 @@
}
// The second argument needs to be a constant int
+ Arg = TheCall->getArg(1);
llvm::APSInt Value;
- if (!TheCall->getArg(1)->isIntegerConstantExpr(Value, Context)) {
+ if (!Arg->isIntegerConstantExpr(Value, Context)) {
Diag(Arg->getBeginLoc(), diag::err_preserve_field_info_not_const)
<< 2 << Arg->getSourceRange();
return true;
Index: clang/lib/CodeGen/CGBuiltin.cpp
===================================================================
--- clang/lib/CodeGen/CGBuiltin.cpp
+++ clang/lib/CodeGen/CGBuiltin.cpp
@@ -10459,33 +10459,103 @@
Value *CodeGenFunction::EmitBPFBuiltinExpr(unsigned BuiltinID,
const CallExpr *E) {
- assert(BuiltinID == BPF::BI__builtin_preserve_field_info &&
- "unexpected ARM builtin");
+ assert((BuiltinID == BPF::BI__builtin_preserve_field_info ||
+ BuiltinID == BPF::BI__builtin_btf_type_id) &&
+ "unexpected BPF builtin");
- const Expr *Arg = E->getArg(0);
- bool IsBitField = Arg->IgnoreParens()->getObjectKind() == OK_BitField;
+ switch (BuiltinID) {
+ default:
+ llvm_unreachable("Unexpected BPF builtin");
+ case BPF::BI__builtin_preserve_field_info: {
+ const Expr *Arg = E->getArg(0);
+ bool IsBitField = Arg->IgnoreParens()->getObjectKind() == OK_BitField;
- if (!getDebugInfo()) {
- CGM.Error(E->getExprLoc(), "using builtin_preserve_field_info() without -g");
- return IsBitField ? EmitLValue(Arg).getBitFieldPointer()
- : EmitLValue(Arg).getPointer(*this);
- }
+ if (!getDebugInfo()) {
+ CGM.Error(E->getExprLoc(),
+ "using __builtin_preserve_field_info() without -g");
+ return IsBitField ? EmitLValue(Arg).getBitFieldPointer()
+ : EmitLValue(Arg).getPointer(*this);
+ }
+
+ // Enable underlying preserve_*_access_index() generation.
+ bool OldIsInPreservedAIRegion = IsInPreservedAIRegion;
+ IsInPreservedAIRegion = true;
+ Value *FieldAddr = IsBitField ? EmitLValue(Arg).getBitFieldPointer()
+ : EmitLValue(Arg).getPointer(*this);
+ IsInPreservedAIRegion = OldIsInPreservedAIRegion;
+
+ ConstantInt *C = cast<ConstantInt>(EmitScalarExpr(E->getArg(1)));
+ Value *InfoKind = ConstantInt::get(Int64Ty, C->getSExtValue());
+
+ // Built the IR for the preserve_field_info intrinsic.
+ llvm::Function *FnGetFieldInfo = llvm::Intrinsic::getDeclaration(
+ &CGM.getModule(), llvm::Intrinsic::bpf_preserve_field_info,
+ {FieldAddr->getType()});
+ return Builder.CreateCall(FnGetFieldInfo, {FieldAddr, InfoKind});
+ }
+ case BPF::BI__builtin_btf_type_id: {
+ Value *FieldVal = nullptr;
+
+ // The LValue cannot be converted Value in order to be used as the function
+ // parameter. If it is a structure, it is the "alloca" result of the LValue
+ // (a pointer) is used in the parameter. If it is a simple type,
+ // the value will be loaded from its corresponding "alloca" and used as
+ // the parameter. In our case, let us just get a pointer of the LValue
+ // since we do not really use the parameter. The purpose of parameter
+ // is to prevent the generated IR llvm.bpf.btf.type.id intrinsic call,
+ // which carries metadata, from being changed.
+ bool IsLValue = E->getArg(0)->isLValue();
+ if (IsLValue)
+ FieldVal = EmitLValue(E->getArg(0)).getPointer(*this);
+ else
+ FieldVal = EmitScalarExpr(E->getArg(0));
- // Enable underlying preserve_*_access_index() generation.
- bool OldIsInPreservedAIRegion = IsInPreservedAIRegion;
- IsInPreservedAIRegion = true;
- Value *FieldAddr = IsBitField ? EmitLValue(Arg).getBitFieldPointer()
- : EmitLValue(Arg).getPointer(*this);
- IsInPreservedAIRegion = OldIsInPreservedAIRegion;
+ if (!getDebugInfo()) {
+ CGM.Error(E->getExprLoc(), "using __builtin_btf_type_id() without -g");
+ return nullptr;
+ }
- ConstantInt *C = cast<ConstantInt>(EmitScalarExpr(E->getArg(1)));
- Value *InfoKind = ConstantInt::get(Int64Ty, C->getSExtValue());
+ // Generate debuginfo type for the first argument.
+ llvm::DIType *DbgInfo =
+ getDebugInfo()->getOrCreateStandaloneType(E->getArg(0)->getType(),
+ E->getArg(0)->getExprLoc());
- // Built the IR for the preserve_field_info intrinsic.
- llvm::Function *FnGetFieldInfo = llvm::Intrinsic::getDeclaration(
- &CGM.getModule(), llvm::Intrinsic::bpf_preserve_field_info,
- {FieldAddr->getType()});
- return Builder.CreateCall(FnGetFieldInfo, {FieldAddr, InfoKind});
+ ConstantInt *Flag = cast<ConstantInt>(EmitScalarExpr(E->getArg(1)));
+ Value *FlagValue = ConstantInt::get(Int64Ty, Flag->getSExtValue());
+
+ // Built the IR for the btf_type_id intrinsic.
+ //
+ // In the above, we converted LValue argument to a pointer to LValue.
+ // For example, the following
+ // int v;
+ // C1: __builtin_btf_type_id(v, flag);
+ // will be converted to
+ // L1: llvm.bpf.btf.type.id(&v, flag)
+ // This makes it hard to differentiate from
+ // C2: __builtin_btf_type_id(&v, flag);
+ // to
+ // L2: llvm.bpf.btf.type.id(&v, flag)
+ //
+ // If both C1 and C2 are present in the code, the llvm may later
+ // on do CSE on L1 and L2, which will result in incorrect tagged types.
+ //
+ // The C1->L1 transformation only happens if the argument of
+ // __builtin_btf_type_id() is a LValue. So Let us put whether
+ // the argument is an LValue or not into generated IR. This should
+ // prevent potential CSE from causing debuginfo type loss.
+ //
+ // The generated IR intrinsics will hence look like
+ // L1: llvm.bpf.btf.type.id(&v, 1, flag) !di_type_for_{v};
+ // L2: llvm.bpf.btf.type.id(&v, 0, flag) !di_type_for_{&v};
+ Constant *CV = ConstantInt::get(IntTy, IsLValue);
+ llvm::Function *FnBtfTypeId = llvm::Intrinsic::getDeclaration(
+ &CGM.getModule(), llvm::Intrinsic::bpf_btf_type_id,
+ {FieldVal->getType(), CV->getType()});
+ CallInst *Fn = Builder.CreateCall(FnBtfTypeId, {FieldVal, CV, FlagValue});
+ Fn->setMetadata(LLVMContext::MD_preserve_access_index, DbgInfo);
+ return Fn;
+ }
+ }
}
llvm::Value *CodeGenFunction::
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -10726,6 +10726,8 @@
"__builtin_preserve_field_info argument %0 not a field access">;
def err_preserve_field_info_not_const: Error<
"__builtin_preserve_field_info argument %0 not a constant">;
+def err_btf_type_id_not_const: Error<
+ "__builtin_btf_type_id argument %0 not a constant">;
def err_bit_cast_non_trivially_copyable : Error<
"__builtin_bit_cast %select{source|destination}0 type must be trivially copyable">;
Index: clang/include/clang/Basic/BuiltinsBPF.def
===================================================================
--- clang/include/clang/Basic/BuiltinsBPF.def
+++ clang/include/clang/Basic/BuiltinsBPF.def
@@ -20,5 +20,8 @@
// Get record field information.
TARGET_BUILTIN(__builtin_preserve_field_info, "Ui.", "t", "")
+// Get BTF type id.
+TARGET_BUILTIN(__builtin_btf_type_id, "Ui.", "t", "")
+
#undef BUILTIN
#undef TARGET_BUILTIN
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits