[llvm] [AsmParser] Add missing globals declarations in incomplete IR mode (PR #79855)

Nikita Popov via llvm-commits llvm-commits at lists.llvm.org
Mon Jan 29 08:13:35 PST 2024


https://github.com/nikic created https://github.com/llvm/llvm-project/pull/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 https://github.com/llvm/llvm-project/pull/78421.

>From 28c4c3f0879b9119266630d3ded6a02a9492f83b Mon Sep 17 00:00:00 2001
From: Nikita Popov <npopov at redhat.com>
Date: Mon, 29 Jan 2024 17:07:37 +0100
Subject: [PATCH] [AsmParser] Add missing globals declarations in incomplete IR
 mode

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).
---
 llvm/lib/AsmParser/LLParser.cpp               | 44 ++++++++++++++-----
 .../Assembler/incomplete-ir-declarations.ll   | 20 +++++++++
 2 files changed, 52 insertions(+), 12 deletions(-)
 create mode 100644 llvm/test/Assembler/incomplete-ir-declarations.ll

diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp
index d6c5993797de111..e111ca9c7e6b5ff 100644
--- a/llvm/lib/AsmParser/LLParser.cpp
+++ b/llvm/lib/AsmParser/LLParser.cpp
@@ -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()) {
@@ -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);
     }
diff --git a/llvm/test/Assembler/incomplete-ir-declarations.ll b/llvm/test/Assembler/incomplete-ir-declarations.ll
new file mode 100644
index 000000000000000..6cdff80883c272d
--- /dev/null
+++ b/llvm/test/Assembler/incomplete-ir-declarations.ll
@@ -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
+}



More information about the llvm-commits mailing list