aboutsummaryrefslogtreecommitdiff
path: root/backend/unittests/MIR/EmbeddedIRTest.cpp
diff options
context:
space:
mode:
authorRichard-Qin-X <richard.qin.001@gmail.com>2026-08-01 17:10:33 +0800
committerRichard-Qin-X <richard.qin.001@gmail.com>2026-08-01 17:10:33 +0800
commit77d76b73a614820bf45024b053db376f35b06d10 (patch)
tree122fa035693683366ad51c0d57b7c9c91f5563b5 /backend/unittests/MIR/EmbeddedIRTest.cpp
parent85eed8fb2d6563fea750f7b55730c9feaecc5898 (diff)
downloadrcc-project-77d76b73a614820bf45024b053db376f35b06d10.tar.gz
rcc-project-77d76b73a614820bf45024b053db376f35b06d10.tar.xz
[MIR] Add compilation unit reference context
Diffstat (limited to 'backend/unittests/MIR/EmbeddedIRTest.cpp')
-rw-r--r--backend/unittests/MIR/EmbeddedIRTest.cpp133
1 files changed, 133 insertions, 0 deletions
diff --git a/backend/unittests/MIR/EmbeddedIRTest.cpp b/backend/unittests/MIR/EmbeddedIRTest.cpp
new file mode 100644
index 0000000..58c6162
--- /dev/null
+++ b/backend/unittests/MIR/EmbeddedIRTest.cpp
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "rcc/MIR/EmbeddedIR.h"
+
+#include <iostream>
+#include <string>
+#include <string_view>
+
+namespace {
+
+int fail(const char *Message) {
+ std::cerr << Message << '\n';
+ return 1;
+}
+
+int testReferenceIndex() {
+ const std::string Input = "; @commented = global i8 0\n"
+ "target datalayout = \"e-m:e-p:64:64\"\n"
+ "target triple = \"riscv64-unknown-linux-gnu\"\n"
+ "@ordinary = global i32 0\n"
+ "@\"space\\20global\" = external global i8\n"
+ "declare void @declared()\n"
+ "define void @\"function\\20name\"(\n"
+ " { i32, i32 } %argument\n"
+ ") {\n"
+ "entry:\n"
+ " %slot = alloca i32, align 4\n"
+ " %not_an_alloca = add i32 1, 2\n"
+ " br label %\"exit\\20block\"\n"
+ "\"exit\\20block\":\n"
+ " ret void\n"
+ "}\n"
+ "!0 = !{}\n"
+ "!llvm.module.flags = !{!0}\n";
+
+ rcc::mir::EmbeddedIRIndexResult Result = rcc::mir::indexEmbeddedIR("embedded.ll", Input);
+ const rcc::mir::EmbeddedIRReferenceIndex *Index = Result.index();
+ if (!Result.succeeded() || Index == nullptr)
+ return fail("Embedded IR indexing must accept the MIR reference subset");
+ if (Index->source().text() != Input)
+ return fail("Embedded IR indexing must preserve the original text");
+ if (Index->targetTriple() != std::optional<std::string_view>("riscv64-unknown-linux-gnu") ||
+ Index->dataLayout() != std::optional<std::string_view>("e-m:e-p:64:64"))
+ return fail("Embedded IR indexing must preserve target facts");
+ if (Index->functions().size() != 2 || Index->function("declared") == nullptr ||
+ Index->function("declared")->isDefinition())
+ return fail("Embedded IR indexing must retain function declarations");
+
+ const rcc::mir::EmbeddedIRFunction *Function = Index->function("function name");
+ if (Function == nullptr || !Function->isDefinition() || Function->blocks().size() != 2)
+ return fail("Embedded IR indexing must decode function and block identifiers");
+ if (Function->block("entry") == nullptr || Function->block("exit block") == nullptr)
+ return fail("Embedded IR block lookup must use decoded names");
+ if (Function->allocas().size() != 1 || Function->alloca("slot") == nullptr ||
+ Function->alloca("not_an_alloca") != nullptr)
+ return fail("Embedded IR indexing must distinguish named allocas from other local values");
+
+ if (!Index->hasGlobalValue("ordinary") || !Index->hasGlobalValue("space global") ||
+ !Index->hasGlobalValue("declared") || Index->hasGlobalValue("commented"))
+ return fail("Embedded IR global lookup must include objects and functions but ignore comments");
+ if (Index->metadata("0") == nullptr || Index->metadata("llvm.module.flags") == nullptr ||
+ Index->metadataDefinitions().size() != 2)
+ return fail("Embedded IR indexing must retain numeric and named metadata definitions");
+ return 0;
+}
+
+int testDeclarationDefinitionMerge() {
+ const std::string Input = "declare void @shared()\n"
+ "define void @shared() {\n"
+ "0:\n"
+ " ret void\n"
+ "}\n";
+ rcc::mir::EmbeddedIRIndexResult Result = rcc::mir::indexEmbeddedIR("merge.ll", Input);
+ const rcc::mir::EmbeddedIRReferenceIndex *Index = Result.index();
+ if (!Result.succeeded() || Index == nullptr || Index->functions().size() != 1)
+ return fail("Embedded IR indexing must merge a declaration with its definition");
+ const rcc::mir::EmbeddedIRFunction *Function = Index->function("shared");
+ if (Function == nullptr || !Function->isDefinition() || Function->block("0") == nullptr)
+ return fail("A later definition must complete its indexed declaration");
+ return 0;
+}
+
+bool hasSingleDiagnostic(const rcc::mir::EmbeddedIRIndexResult &Result, std::string_view Message) {
+ return !Result.succeeded() && Result.diagnostics().size() == 1 &&
+ Result.diagnostics()[0].Message == Message;
+}
+
+int testDiagnostics() {
+ auto DuplicateBlock =
+ rcc::mir::indexEmbeddedIR("blocks.ll", "define void @f() {\nentry:\nentry:\n ret void\n}\n");
+ if (!hasSingleDiagnostic(DuplicateBlock, "duplicate embedded LLVM IR basic block label") ||
+ DuplicateBlock.diagnostics()[0].Range.Begin.Line != 3)
+ return fail("Embedded IR indexing must reject a duplicate block at its second label");
+
+ auto BadEscape = rcc::mir::indexEmbeddedIR("escape.ll", "@\"bad\\q0\" = global i8 0\n");
+ if (!hasSingleDiagnostic(BadEscape,
+ "LLVM IR identifier escape must contain two hexadecimal digits"))
+ return fail("Embedded IR indexing must reject malformed identifier escapes");
+
+ auto Unterminated =
+ rcc::mir::indexEmbeddedIR("function.ll", "define void @f() {\nentry:\n ret void\n");
+ if (!hasSingleDiagnostic(Unterminated,
+ "embedded LLVM IR function definition has no closing brace"))
+ return fail("Embedded IR indexing must reject an unterminated function definition");
+
+ auto DuplicateTriple =
+ rcc::mir::indexEmbeddedIR("triple.ll", "target triple = \"riscv64-unknown-linux-gnu\"\n"
+ "target triple = \"riscv64-unknown-linux-gnu\"\n");
+ if (!hasSingleDiagnostic(DuplicateTriple, "duplicate embedded LLVM IR target triple"))
+ return fail("Embedded IR indexing must reject duplicate target facts");
+
+ auto NameConflict =
+ rcc::mir::indexEmbeddedIR("names.ll", "@same = global i32 0\ndeclare void @same()\n");
+ if (!hasSingleDiagnostic(NameConflict,
+ "embedded LLVM IR name is used by both a function and a global object"))
+ return fail("Embedded IR indexing must reject conflicting global value kinds");
+
+ const std::string NulInput("target triple = \"x\"\0", 20);
+ auto Nul = rcc::mir::indexEmbeddedIR("nul.ll", NulInput);
+ if (!hasSingleDiagnostic(Nul, "NUL bytes are not allowed in embedded LLVM IR"))
+ return fail("Embedded IR indexing must reject embedded NUL bytes");
+ return 0;
+}
+
+} // namespace
+
+int main() {
+ if (const int Result = testReferenceIndex(); Result != 0)
+ return Result;
+ if (const int Result = testDeclarationDefinitionMerge(); Result != 0)
+ return Result;
+ return testDiagnostics();
+}