Skip to content

Commit

Permalink
[CIR][Lowering] Emit llvm.global_ctors list (#240)
Browse files Browse the repository at this point in the history
Creating the `llvm.global_ctors` list to hold all global dynamic
initializers. The list has the following format:

```
%0 = type { i32, ptr, ptr }
@llvm.global_ctors = appending global [1 x %0] [%0 { i32 65535, ptr @ctor, ptr @DaTa }]
```

The list will be converted to `.init_array` for ELF by LLVM which will
be loaded and executed by the C++ runtime.
  • Loading branch information
htyu authored Aug 23, 2023
1 parent cb2517c commit 2fae9b7
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 4 deletions.
21 changes: 21 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
Original file line number Diff line number Diff line change
Expand Up @@ -452,4 +452,25 @@ def OptNoneAttr : CIRUnitAttr<"OptNone", "optnone"> {
let storageType = [{ OptNoneAttr }];
}

def GlobalCtorAttr : CIR_Attr<"GlobalCtor", "globalCtor"> {
let summary = "Indicates a function is a global constructor.";
let description = [{
Describing a global constructor with an optional priority.
}];
let parameters = (ins "StringAttr":$name,
OptionalParameter<"std::optional<int>">:$priority);
let assemblyFormat = [{
`<`
$name
(`,` $priority^)?
`>`
}];
let builders = [
AttrBuilder<(ins "StringRef":$name,
CArg<"std::optional<int>", "{}">:$priority), [{
return $_get($_ctxt, StringAttr::get($_ctxt, name), priority);
}]>
];
let skipDefaultBuilders = 1;
}
#endif // MLIR_CIR_DIALECT_CIR_ATTRS
18 changes: 16 additions & 2 deletions clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include "PassDetail.h"
#include "mlir/Dialect/Func/IR/FuncOps.h"
#include "mlir/IR/BuiltinAttributes.h"
#include "mlir/IR/Region.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Mangle.h"
Expand Down Expand Up @@ -125,6 +126,8 @@ void LoweringPreparePass::lowerGlobalOp(GlobalOp op) {
ctorRegion.getBlocks().clear();

// Add a function call to the variable initialization function.
assert(!op.getAst()->getAstDecl()->getAttr<clang::InitPriorityAttr>() &&
"custom initialization priority NYI");
dynamicInitializers.push_back(f);
}
}
Expand All @@ -133,6 +136,17 @@ void LoweringPreparePass::buildCXXGlobalInitFunc() {
if (dynamicInitializers.empty())
return;

SmallVector<mlir::Attribute, 4> attrs;
for (auto &f : dynamicInitializers) {
// TODO: handle globals with a user-specified initialzation priority.
auto ctorAttr =
mlir::cir::GlobalCtorAttr::get(&getContext(), f.getName());
attrs.push_back(ctorAttr);
}

theModule->setAttr("cir.globalCtors",
mlir::ArrayAttr::get(&getContext(), attrs));

SmallString<256> fnName;
// Include the filename in the symbol name. Including "sub_" matches gcc
// and makes sure these symbols appear lexicographically behind the symbols
Expand Down Expand Up @@ -161,9 +175,9 @@ void LoweringPreparePass::buildCXXGlobalInitFunc() {
builder.getContext(), mlir::cir::GlobalLinkageKind::ExternalLinkage));
mlir::SymbolTable::setSymbolVisibility(
f, mlir::SymbolTable::Visibility::Private);
mlir::NamedAttrList attrs;
mlir::NamedAttrList extraAttrs;
f.setExtraAttrsAttr(mlir::cir::ExtraFuncAttributesAttr::get(
builder.getContext(), attrs.getDictionary(builder.getContext())));
builder.getContext(), extraAttrs.getDictionary(builder.getContext())));

builder.setInsertionPointToStart(f.addEntryBlock());
for (auto &f : dynamicInitializers) {
Expand Down
77 changes: 77 additions & 0 deletions clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/Sequence.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/Support/Casting.h"
Expand Down Expand Up @@ -1830,6 +1831,79 @@ mlir::LLVMTypeConverter prepareTypeConverter(mlir::MLIRContext *ctx) {
}
} // namespace

static void buildCtorList(mlir::ModuleOp module) {
llvm::SmallVector<std::pair<StringRef, int>, 2> globalCtors;
for (auto namedAttr : module->getAttrs()) {
if (namedAttr.getName() == "cir.globalCtors") {
for (auto attr : namedAttr.getValue().cast<mlir::ArrayAttr>()) {
assert(attr.isa<mlir::cir::GlobalCtorAttr>() &&
"must be a GlobalCtorAttr");
if (auto ctorAttr = attr.cast<mlir::cir::GlobalCtorAttr>()) {
// default priority is 65536
int priority = 65536;
if (ctorAttr.getPriority())
priority = *ctorAttr.getPriority();
globalCtors.emplace_back(ctorAttr.getName(), priority);
}
}
break;
}
}

if (globalCtors.empty())
return;

mlir::OpBuilder builder(module.getContext());
builder.setInsertionPointToEnd(&module.getBodyRegion().back());

// Create a global array llvm.global_ctors with element type of
// struct { i32, ptr, ptr }
auto CtorPFTy = mlir::LLVM::LLVMPointerType::get(builder.getContext());
llvm::SmallVector<mlir::Type> CtorStructFields;
CtorStructFields.push_back(builder.getI32Type());
CtorStructFields.push_back(CtorPFTy);
CtorStructFields.push_back(CtorPFTy);

auto CtorStructTy = mlir::LLVM::LLVMStructType::getLiteral(
builder.getContext(), CtorStructFields);
auto CtorStructArrayTy =
mlir::LLVM::LLVMArrayType::get(CtorStructTy, globalCtors.size());

auto loc = module.getLoc();
auto newGlobalOp = builder.create<mlir::LLVM::GlobalOp>(
loc, CtorStructArrayTy, true, mlir::LLVM::Linkage::Appending,
"llvm.global_ctors", mlir::Attribute());

newGlobalOp.getRegion().push_back(new mlir::Block());
builder.setInsertionPointToEnd(newGlobalOp.getInitializerBlock());

mlir::Value result = builder.create<mlir::LLVM::UndefOp>(
loc, CtorStructArrayTy);

for (uint64_t I = 0; I < globalCtors.size(); I++) {
auto fn = globalCtors[I];
mlir::Value structInit =
builder.create<mlir::LLVM::UndefOp>(loc, CtorStructTy);
mlir::Value initPriority =
builder.create<mlir::LLVM::ConstantOp>(loc, CtorStructFields[0], fn.second);
mlir::Value initFuncAddr = builder.create<mlir::LLVM::AddressOfOp>(
loc, CtorStructFields[1], fn.first);
mlir::Value initAssociate =
builder.create<mlir::LLVM::NullOp>(loc, CtorStructFields[2]);
structInit = builder.create<mlir::LLVM::InsertValueOp>(loc, structInit,
initPriority, 0);
structInit = builder.create<mlir::LLVM::InsertValueOp>(loc, structInit,
initFuncAddr, 1);
// TODO: handle associated data for initializers.
structInit = builder.create<mlir::LLVM::InsertValueOp>(loc, structInit,
initAssociate, 2);
result =
builder.create<mlir::LLVM::InsertValueOp>(loc, result, structInit, I);
}

builder.create<mlir::LLVM::ReturnOp>(loc, result);
}

void ConvertCIRToLLVMPass::runOnOperation() {
auto module = getOperation();

Expand Down Expand Up @@ -1871,6 +1945,9 @@ void ConvertCIRToLLVMPass::runOnOperation() {

if (failed(applyPartialConversion(module, target, std::move(patterns))))
signalPassFailure();

// Emit the llvm.global_ctors array.
buildCtorList(module);
}

std::unique_ptr<mlir::Pass> createConvertCIRToLLVMPass() {
Expand Down
4 changes: 2 additions & 2 deletions clang/test/CIR/CodeGen/static.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ static Init __ioinit2(false);
// BEFORE-NEXT: }


// AFTER: module {{.*}} {
// AFTER: module {{.*}} attributes {{.*}}cir.globalCtors = [#cir.globalCtor<"__cxx_global_var_init">, #cir.globalCtor<"__cxx_global_var_init.1">]
// AFTER-NEXT: cir.func private @_ZN4InitC1Eb(!cir.ptr<!ty_22class2EInit22>, !cir.bool)
// AFTER-NEXT: cir.global "private" internal @_ZL8__ioinit = #cir.zero : !ty_22class2EInit22 {ast = #cir.vardecl.ast}
// AFTER-NEXT: cir.func internal private @__cxx_global_var_init()
Expand All @@ -50,9 +50,9 @@ static Init __ioinit2(false);
// AFTER-NEXT: cir.call @__cxx_global_var_init.1() : () -> ()
// AFTER-NEXT: cir.return


// LLVM: @_ZL8__ioinit = internal global %class.Init zeroinitializer
// LLVM: @_ZL9__ioinit2 = internal global %class.Init zeroinitializer
// LLVM: @llvm.global_ctors = appending constant [2 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65536, ptr @__cxx_global_var_init, ptr null }, { i32, ptr, ptr } { i32 65536, ptr @__cxx_global_var_init.1, ptr null }]
// LLVM: define internal void @__cxx_global_var_init()
// LLVM-NEXT: call void @_ZN4InitC1Eb(ptr @_ZL8__ioinit, i8 1)
// LLVM-NEXT: ret void
Expand Down

0 comments on commit 2fae9b7

Please sign in to comment.