[llvm] c087fff - [llubi] Add basic support for vector and aggregate ops (#181544)
via llvm-commits
llvm-commits at lists.llvm.org
Sun Feb 15 10:38:59 PST 2026
Author: Yingwei Zheng
Date: 2026-02-15T18:38:54Z
New Revision: c087fff25a2420ff7023445973e72f3261385b5d
URL: https://github.com/llvm/llvm-project/commit/c087fff25a2420ff7023445973e72f3261385b5d
DIFF: https://github.com/llvm/llvm-project/commit/c087fff25a2420ff7023445973e72f3261385b5d.diff
LOG: [llubi] Add basic support for vector and aggregate ops (#181544)
Added:
llvm/test/tools/llubi/struct.ll
llvm/test/tools/llubi/vector.ll
Modified:
llvm/tools/llubi/lib/Interpreter.cpp
llvm/tools/llubi/lib/Value.h
Removed:
################################################################################
diff --git a/llvm/test/tools/llubi/struct.ll b/llvm/test/tools/llubi/struct.ll
new file mode 100644
index 0000000000000..ce469a7b6d320
--- /dev/null
+++ b/llvm/test/tools/llubi/struct.ll
@@ -0,0 +1,20 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: llubi --verbose < %s 2>&1 | FileCheck %s
+
+define void @main() {
+ %insert = insertvalue { i32, i32 } poison, i32 42, 0
+ %extract = extractvalue { i32, i32 } %insert, 0
+ %insert_update = insertvalue { i32, i32 } zeroinitializer, i32 1, 1
+
+ %insert_nested = insertvalue { i32, {{ i32, i32 }} } poison, { i32, i32 } zeroinitializer, 1, 0
+ %extract_nested = extractvalue { i32, {{ i32, i32 }} } %insert_nested, 1, 0, 1
+ ret void
+}
+; CHECK: Entering function: main
+; CHECK-NEXT: %insert = insertvalue { i32, i32 } poison, i32 42, 0 => { i32 42, poison }
+; CHECK-NEXT: %extract = extractvalue { i32, i32 } %insert, 0 => i32 42
+; CHECK-NEXT: %insert_update = insertvalue { i32, i32 } zeroinitializer, i32 1, 1 => { i32 0, i32 1 }
+; CHECK-NEXT: %insert_nested = insertvalue { i32, { { i32, i32 } } } poison, { i32, i32 } zeroinitializer, 1, 0 => { poison, { { i32 0, i32 0 } } }
+; CHECK-NEXT: %extract_nested = extractvalue { i32, { { i32, i32 } } } %insert_nested, 1, 0, 1 => i32 0
+; CHECK-NEXT: ret void
+; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/vector.ll b/llvm/test/tools/llubi/vector.ll
new file mode 100644
index 0000000000000..927adafd0dcb6
--- /dev/null
+++ b/llvm/test/tools/llubi/vector.ll
@@ -0,0 +1,35 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: llubi --verbose < %s 2>&1 | FileCheck %s
+
+define void @main() {
+ %insert = insertelement <4 x i32> poison, i32 42, i32 0
+ %splat = shufflevector <4 x i32> %insert, <4 x i32> poison, <4 x i32> zeroinitializer
+ %extract = extractelement <4 x i32> %splat, i32 3
+
+ %insert_poison = insertelement <4 x i32> zeroinitializer, i32 42, i32 5
+ %extract_poison = extractelement <4 x i32> zeroinitializer, i32 5
+
+ %scalable_insert = insertelement <vscale x 4 x i32> poison, i32 42, i32 0
+ %scalable_splat = shufflevector <vscale x 4 x i32> %scalable_insert, <vscale x 4 x i32> poison, <vscale x 4 x i32> zeroinitializer
+ %scalable_extract = extractelement <vscale x 4 x i32> %scalable_splat, i32 3
+
+ %insert_update = insertelement <4 x i32> <i32 0, i32 poison, i32 2, i32 3>, i32 1, i32 1
+ %shuffle_mixed = shufflevector <4 x i32> <i32 0, i32 poison, i32 2, i32 3>, <4 x i32> <i32 4, i32 5, i32 6, i32 7>, <4 x i32> <i32 7, i32 1, i32 poison, i32 0>
+ %shuffle_mismatch = shufflevector <4 x i32> <i32 0, i32 1, i32 2, i32 3>, <4 x i32> <i32 4, i32 5, i32 6, i32 7>, <3 x i32> <i32 7, i32 poison, i32 3>
+
+ ret void
+}
+; CHECK: Entering function: main
+; CHECK-NEXT: %insert = insertelement <4 x i32> poison, i32 42, i32 0 => { i32 42, poison, poison, poison }
+; CHECK-NEXT: %splat = shufflevector <4 x i32> %insert, <4 x i32> poison, <4 x i32> zeroinitializer => { i32 42, i32 42, i32 42, i32 42 }
+; CHECK-NEXT: %extract = extractelement <4 x i32> %splat, i32 3 => i32 42
+; CHECK-NEXT: %insert_poison = insertelement <4 x i32> zeroinitializer, i32 42, i32 5 => { poison, poison, poison, poison }
+; CHECK-NEXT: %extract_poison = extractelement <4 x i32> zeroinitializer, i32 5 => poison
+; CHECK-NEXT: %scalable_insert = insertelement <vscale x 4 x i32> poison, i32 42, i32 0 => { i32 42, poison, poison, poison, poison, poison, poison, poison, poison, poison, poison, poison, poison, poison, poison, poison }
+; CHECK-NEXT: %scalable_splat = shufflevector <vscale x 4 x i32> %scalable_insert, <vscale x 4 x i32> poison, <vscale x 4 x i32> zeroinitializer => { i32 42, i32 42, i32 42, i32 42, i32 42, i32 42, i32 42, i32 42, i32 42, i32 42, i32 42, i32 42, i32 42, i32 42, i32 42, i32 42 }
+; CHECK-NEXT: %scalable_extract = extractelement <vscale x 4 x i32> %scalable_splat, i32 3 => i32 42
+; CHECK-NEXT: %insert_update = insertelement <4 x i32> <i32 0, i32 poison, i32 2, i32 3>, i32 1, i32 1 => { i32 0, i32 1, i32 2, i32 3 }
+; CHECK-NEXT: %shuffle_mixed = shufflevector <4 x i32> <i32 0, i32 poison, i32 2, i32 3>, <4 x i32> <i32 4, i32 5, i32 6, i32 7>, <4 x i32> <i32 7, i32 1, i32 poison, i32 0> => { i32 7, poison, poison, i32 0 }
+; CHECK-NEXT: %shuffle_mismatch = shufflevector <4 x i32> <i32 0, i32 1, i32 2, i32 3>, <4 x i32> <i32 4, i32 5, i32 6, i32 7>, <3 x i32> <i32 7, i32 poison, i32 3> => { i32 7, poison, i32 3 }
+; CHECK-NEXT: ret void
+; CHECK-NEXT: Exiting function: main
diff --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index 9cc4adc0b4310..a5d39012e5c53 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -719,6 +719,70 @@ class InstExecutor : public InstVisitor<InstExecutor, void> {
Status = false;
}
+ void visitExtractValueInst(ExtractValueInst &EVI) {
+ auto &Res = getValue(EVI.getAggregateOperand());
+ const AnyValue *Pos = &Res;
+ for (unsigned Idx : EVI.indices())
+ Pos = &Pos->asAggregate()[Idx];
+ setResult(EVI, *Pos);
+ }
+
+ void visitInsertValueInst(InsertValueInst &IVI) {
+ AnyValue Res = getValue(IVI.getAggregateOperand());
+ AnyValue *Pos = &Res;
+ for (unsigned Idx : IVI.indices())
+ Pos = &Pos->asAggregate()[Idx];
+ *Pos = getValue(IVI.getInsertedValueOperand());
+ setResult(IVI, std::move(Res));
+ }
+
+ void visitInsertElementInst(InsertElementInst &IEI) {
+ auto Res = getValue(IEI.getOperand(0));
+ auto &ResVec = Res.asAggregate();
+ auto &Idx = getValue(IEI.getOperand(2));
+ if (Idx.isPoison() || Idx.asInteger().uge(ResVec.size())) {
+ setResult(IEI, AnyValue::getPoisonValue(Ctx, IEI.getType()));
+ return;
+ }
+ ResVec[Idx.asInteger().getZExtValue()] = getValue(IEI.getOperand(1));
+ setResult(IEI, std::move(Res));
+ }
+
+ void visitExtractElementInst(ExtractElementInst &EEI) {
+ auto &SrcVec = getValue(EEI.getOperand(0)).asAggregate();
+ auto &Idx = getValue(EEI.getOperand(1));
+ if (Idx.isPoison() || Idx.asInteger().uge(SrcVec.size())) {
+ setResult(EEI, AnyValue::getPoisonValue(Ctx, EEI.getType()));
+ return;
+ }
+ setResult(EEI, SrcVec[Idx.asInteger().getZExtValue()]);
+ }
+
+ void visitShuffleVectorInst(ShuffleVectorInst &SVI) {
+ auto &LHSVec = getValue(SVI.getOperand(0)).asAggregate();
+ auto &RHSVec = getValue(SVI.getOperand(1)).asAggregate();
+ uint32_t Size = cast<VectorType>(SVI.getOperand(0)->getType())
+ ->getElementCount()
+ .getKnownMinValue();
+ std::vector<AnyValue> Res;
+ uint32_t DstLen = Ctx.getEVL(SVI.getType()->getElementCount());
+ Res.reserve(DstLen);
+ uint32_t Stride = SVI.getShuffleMask().size();
+ // For scalable vectors, we need to repeat the shuffle mask until we fill
+ // the destination vector.
+ for (uint32_t Off = 0; Off != DstLen; Off += Stride) {
+ for (int Idx : SVI.getShuffleMask()) {
+ if (Idx == PoisonMaskElem)
+ Res.push_back(AnyValue::poison());
+ else if (Idx < static_cast<int>(Size))
+ Res.push_back(LHSVec[Idx]);
+ else
+ Res.push_back(RHSVec[Idx - Size]);
+ }
+ }
+ setResult(SVI, std::move(Res));
+ }
+
/// This function implements the main interpreter loop.
/// It handles function calls in a non-recursive manner to avoid stack
/// overflows.
diff --git a/llvm/tools/llubi/lib/Value.h b/llvm/tools/llubi/lib/Value.h
index 22bb074c7b3c4..44fd4727bd78f 100644
--- a/llvm/tools/llubi/lib/Value.h
+++ b/llvm/tools/llubi/lib/Value.h
@@ -137,6 +137,12 @@ class [[nodiscard]] AnyValue {
return AggVal;
}
+ std::vector<AnyValue> &asAggregate() {
+ assert(Kind == StorageKind::Aggregate &&
+ "Expect an aggregate/vector value");
+ return AggVal;
+ }
+
// Helper function for C++ 17 structured bindings.
template <size_t I> const AnyValue &get() const {
assert(Kind == StorageKind::Aggregate &&
More information about the llvm-commits
mailing list