Skip to content

Commit

Permalink
[AsmParser] Add missing globals declarations in incomplete IR mode (l…
Browse files Browse the repository at this point in the history
…lvm#79855)

If `-allow-incomplete-ir` is enabled, automatically insert declarations
for missing globals.

If a global is only used in calls with the same function type, insert a
function declaration with that type.

Otherwise, insert a dummy i8 global. The fallback case could be extended
with various heuristics (e.g. we could look at load/store types), but
I've chosen to keep it simple for now, because I'm unsure to what degree
this would really useful without more experience. I expect that in most
cases the declaration type doesn't really matter (note that the type of
an external global specifies a *minimum* size only, not a precise size).

This is a followup to llvm#78421.
  • Loading branch information
nikic authored Jan 31, 2024
1 parent cb6240d commit 5cc87b4
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 12 deletions.
44 changes: 32 additions & 12 deletions llvm/lib/AsmParser/LLParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -303,15 +303,7 @@ bool LLParser::validateEndOfModule(bool UpgradeDebugInfo) {
"use of undefined comdat '$" +
ForwardRefComdats.begin()->first + "'");

// Automatically create declarations for intrinsics. Intrinsics can only be
// called directly, so the call function type directly determines the
// declaration function type.
for (const auto &[Name, Info] : make_early_inc_range(ForwardRefVals)) {
if (!StringRef(Name).starts_with("llvm."))
continue;

// Don't do anything if the intrinsic is called with different function
// types. This would result in a verifier error anyway.
auto GetCommonFunctionType = [](Value *V) -> FunctionType * {
FunctionType *FTy = nullptr;
for (User *U : V->users()) {
Expand All @@ -322,10 +314,38 @@ bool LLParser::validateEndOfModule(bool UpgradeDebugInfo) {
}
return FTy;
};
if (FunctionType *FTy = GetCommonFunctionType(Info.first)) {
Function *Fn =
Function::Create(FTy, GlobalValue::ExternalLinkage, Name, M);
Info.first->replaceAllUsesWith(Fn);

auto GetDeclarationType = [&](StringRef Name, Value *V) -> Type * {
// Automatically create declarations for intrinsics. Intrinsics can only
// be called directly, so the call function type directly determines the
// declaration function type.
if (Name.starts_with("llvm."))
// Don't do anything if the intrinsic is called with different function
// types. This would result in a verifier error anyway.
return GetCommonFunctionType(V);

if (AllowIncompleteIR) {
// If incomplete IR is allowed, also add declarations for
// non-intrinsics. First check whether this global is only used in
// calls with the same type, in which case we'll insert a function.
if (auto *Ty = GetCommonFunctionType(V))
return Ty;

// Otherwise, fall back to using a dummy i8 type.
return Type::getInt8Ty(Context);
}
return nullptr;
};

if (Type *Ty = GetDeclarationType(Name, Info.first)) {
GlobalValue *GV;
if (auto *FTy = dyn_cast<FunctionType>(Ty))
GV = Function::Create(FTy, GlobalValue::ExternalLinkage, Name, M);
else
GV = new GlobalVariable(*M, Ty, /*isConstant*/ false,
GlobalValue::ExternalLinkage,
/*Initializer*/ nullptr, Name);
Info.first->replaceAllUsesWith(GV);
Info.first->eraseFromParent();
ForwardRefVals.erase(Name);
}
Expand Down
20 changes: 20 additions & 0 deletions llvm/test/Assembler/incomplete-ir-declarations.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
; RUN: opt -S -allow-incomplete-ir < %s | FileCheck %s

; CHECK: @fn2 = external global i8
; CHECK: @g1 = external global i8
; CHECK: @g2 = external global i8
; CHECK: @g3 = external global i8

; CHECK: declare void @fn1(i32)

define ptr @test() {
call void @fn1(i32 0)
call void @fn1(i32 1)
call void @fn2(i32 2)
call void @fn2(i32 2, i32 3)
load i32, ptr @g1
store i32 0, ptr @g1
load i32, ptr @g1
load i64, ptr @g2
ret ptr @g3
}

0 comments on commit 5cc87b4

Please sign in to comment.