Skip to content

Commit

Permalink
[CIR][CIRGen] Support array def after decl with unknown bound (llvm#375)
Browse files Browse the repository at this point in the history
Arrays can be first declared without a known bound, and then defined
with a known bound. For example:

```cpp
extern int data[];

int test() { return data[1]; }

int data[3] {1, 2, 3};
```

Currently `clangir` crashes on generating CIR for this case. This is due
to the type of the `data` definition being different from its
declaration. This patch adds support for such a case.
  • Loading branch information
Lancern authored and lanza committed Mar 20, 2024
1 parent e05262f commit 26a1cd1
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 7 deletions.
49 changes: 43 additions & 6 deletions clang/lib/CIR/CodeGen/CIRGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,8 @@ mlir::Value CIRGenModule::getGlobalValue(const Decl *D) {
mlir::cir::GlobalOp CIRGenModule::createGlobalOp(CIRGenModule &CGM,
mlir::Location loc,
StringRef name, mlir::Type t,
bool isCst) {
bool isCst,
mlir::Operation *insertPoint) {
mlir::cir::GlobalOp g;
auto &builder = CGM.getBuilder();
{
Expand All @@ -486,8 +487,12 @@ mlir::cir::GlobalOp CIRGenModule::createGlobalOp(CIRGenModule &CGM,
builder.setInsertionPoint(curCGF->CurFn);

g = builder.create<mlir::cir::GlobalOp>(loc, name, t, isCst);
if (!curCGF)
CGM.getModule().push_back(g);
if (!curCGF) {
if (insertPoint)
CGM.getModule().insert(insertPoint, g);
else
CGM.getModule().push_back(g);
}

// Default to private until we can judge based on the initializer,
// since MLIR doesn't allow public declarations.
Expand All @@ -501,6 +506,35 @@ void CIRGenModule::setCommonAttributes(GlobalDecl GD, mlir::Operation *GV) {
assert(!UnimplementedFeature::setCommonAttributes());
}

void CIRGenModule::replaceGlobal(mlir::cir::GlobalOp Old,
mlir::cir::GlobalOp New) {
assert(Old.getSymName() == New.getSymName() && "symbol names must match");

// If the types does not match, update all references to Old to the new type.
auto OldTy = Old.getSymType();
auto NewTy = New.getSymType();
if (OldTy != NewTy) {
auto OldSymUses = Old.getSymbolUses(theModule.getOperation());
if (OldSymUses.has_value()) {
for (auto Use : *OldSymUses) {
auto *UserOp = Use.getUser();
assert((isa<mlir::cir::GetGlobalOp>(UserOp) ||
isa<mlir::cir::GlobalOp>(UserOp)) &&
"GlobalOp symbol user is neither a GetGlobalOp nor a GlobalOp");

if (auto GGO = dyn_cast<mlir::cir::GetGlobalOp>(Use.getUser())) {
auto UseOpResultValue = GGO.getAddr();
UseOpResultValue.setType(
mlir::cir::PointerType::get(builder.getContext(), NewTy));
}
}
}
}

// Remove old global from the module.
Old.erase();
}

/// If the specified mangled name is not in the module,
/// create and return an mlir GlobalOp with the specified type (TODO(cir):
/// address space).
Expand Down Expand Up @@ -592,11 +626,14 @@ CIRGenModule::getOrCreateCIRGlobal(StringRef MangledName, mlir::Type Ty,
// mlir::SymbolTable::Visibility::Public is the default, no need to explicitly
// mark it as such.
auto GV = CIRGenModule::createGlobalOp(*this, loc, MangledName, Ty,
/*isConstant=*/false);
/*isConstant=*/false,
/*insertPoint=*/Entry.getOperation());

// If we already created a global with the same mangled name (but different
// type) before, take its name and remove it from its parent.
assert(!Entry && "not implemented");
// type) before, replace it with the new global.
if (Entry) {
replaceGlobal(Entry, GV);
}

// This is the first use or definition of a mangled name. If there is a
// deferred decl with this name, remember that we need to emit it at the end
Expand Down
11 changes: 10 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,8 @@ class CIRGenModule : public CIRGenTypeCache {

static mlir::cir::GlobalOp createGlobalOp(CIRGenModule &CGM,
mlir::Location loc, StringRef name,
mlir::Type t, bool isCst = false);
mlir::Type t, bool isCst = false,
mlir::Operation *insertPoint = nullptr);

/// Return the mlir::Value for the address of the given global variable.
/// If Ty is non-null and if the global doesn't exist, then it will be created
Expand Down Expand Up @@ -445,6 +446,14 @@ class CIRGenModule : public CIRGenTypeCache {
void setGVProperties(mlir::Operation *Op, const NamedDecl *D) const;
void setGVPropertiesAux(mlir::Operation *Op, const NamedDecl *D) const;

/// Replace the present global `Old` with the given global `New`. Their symbol
/// names must match; their types can be different. Usages of the old global
/// will be automatically updated if their types mismatch.
///
/// This function will erase the old global. This function will NOT insert the
/// new global into the module.
void replaceGlobal(mlir::cir::GlobalOp Old, mlir::cir::GlobalOp New);

/// Determine whether the definition must be emitted; if this returns \c
/// false, the definition can be emitted lazily if it's used.
bool MustBeEmitted(const clang::ValueDecl *D);
Expand Down
14 changes: 14 additions & 0 deletions clang/test/CIR/CodeGen/array-unknown-bound.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-cir %s -o - | FileCheck %s

extern int table[];
// CHECK: cir.global external @table = #cir.const_array<[#cir.int<1> : !s32i, #cir.int<2> : !s32i, #cir.int<3> : !s32i]> : !cir.array<!s32i x 3>

int *table_ptr = table;
// CHECK: cir.global external @table_ptr = #cir.global_view<@table> : !cir.ptr<!s32i>

int test() { return table[1]; }
// CHECK: cir.func @_Z4testv() -> !s32i extra( {inline = #cir.inline<no>, optnone = #cir.optnone} ) {
// CHECK-NEXT: %0 = cir.alloca !s32i, cir.ptr <!s32i>, ["__retval"] {alignment = 4 : i64}
// CHECK-NEXT: %1 = cir.get_global @table : cir.ptr <!cir.array<!s32i x 3>>

int table[3] {1, 2, 3};

0 comments on commit 26a1cd1

Please sign in to comment.