From 944f0220602bb6a30af8f3b894499e51a69517ed Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Sun, 2 May 2021 17:39:52 +0200 Subject: [PATCH] interp: support extractvalue/insertvalue with multiple operands TinyGo doesn't emit these instructions, but they can occur as a result of optimizations. --- interp/interpreter.go | 28 ++++++++++++++++++++++------ interp/testdata/basic.ll | 10 ++++++++++ interp/testdata/basic.out.ll | 14 ++++++++++++++ 3 files changed, 46 insertions(+), 6 deletions(-) diff --git a/interp/interpreter.go b/interp/interpreter.go index ec71c56e..c6ce6b7f 100644 --- a/interp/interpreter.go +++ b/interp/interpreter.go @@ -5,6 +5,7 @@ import ( "fmt" "math" "os" + "strconv" "strings" "time" @@ -930,16 +931,31 @@ func (r *runner) runAtRuntime(fn *function, inst instruction, locals []value, me result = r.builder.CreateBitCast(operands[0], inst.llvmInst.Type(), inst.name) case llvm.ExtractValue: indices := inst.llvmInst.Indices() - if len(indices) != 1 { - panic("expected exactly one index") + // Note: the Go LLVM API doesn't support multiple indices, so simulate + // this operation with some extra extractvalue instructions. Hopefully + // this is optimized to a single instruction. + agg := operands[0] + for i := 0; i < len(indices)-1; i++ { + agg = r.builder.CreateExtractValue(agg, int(indices[i]), inst.name+".agg") } - result = r.builder.CreateExtractValue(operands[0], int(indices[0]), inst.name) + result = r.builder.CreateExtractValue(agg, int(indices[len(indices)-1]), inst.name) case llvm.InsertValue: indices := inst.llvmInst.Indices() - if len(indices) != 1 { - panic("expected exactly one index") + // Similar to extractvalue, we're working around a limitation in the Go + // LLVM API here by splitting the insertvalue into multiple instructions + // if there is more than one operand. + agg := operands[0] + aggregates := []llvm.Value{agg} + for i := 0; i < len(indices)-1; i++ { + agg = r.builder.CreateExtractValue(agg, int(indices[i]), inst.name+".agg"+strconv.Itoa(i)) + aggregates = append(aggregates, agg) } - result = r.builder.CreateInsertValue(operands[0], operands[1], int(indices[0]), inst.name) + result = operands[1] + for i := len(indices) - 1; i >= 0; i-- { + agg := aggregates[i] + result = r.builder.CreateInsertValue(agg, result, int(indices[i]), inst.name+".insertvalue"+strconv.Itoa(i)) + } + case llvm.Add: result = r.builder.CreateAdd(operands[0], operands[1], inst.name) case llvm.Sub: diff --git a/interp/testdata/basic.ll b/interp/testdata/basic.ll index e3ff974e..00117058 100644 --- a/interp/testdata/basic.ll +++ b/interp/testdata/basic.ll @@ -8,6 +8,7 @@ target triple = "x86_64--linux" @main.exportedValue = global [1 x i16*] [i16* @main.exposedValue1] @main.exposedValue1 = global i16 0 @main.exposedValue2 = global i16 0 +@main.insertedValue = global {i8, i32, {float, {i64, i16}}} zeroinitializer declare void @runtime.printint64(i64) unnamed_addr @@ -71,6 +72,13 @@ entry: call void @runtime.printint64(i64 %switch1) call void @runtime.printint64(i64 %switch2) + ; Test extractvalue/insertvalue with multiple operands. + %agg = call {i8, i32, {float, {i64, i16}}} @nestedStruct() + %elt = extractvalue {i8, i32, {float, {i64, i16}}} %agg, 2, 1, 0 + call void @runtime.printint64(i64 %elt) + %agg2 = insertvalue {i8, i32, {float, {i64, i16}}} %agg, i64 5, 2, 1, 0 + store {i8, i32, {float, {i64, i16}}} %agg2, {i8, i32, {float, {i64, i16}}}* @main.insertedValue + ret void } @@ -112,3 +120,5 @@ two: otherwise: ret i64 -1 } + +declare {i8, i32, {float, {i64, i16}}} @nestedStruct() diff --git a/interp/testdata/basic.out.ll b/interp/testdata/basic.out.ll index e873b1c8..24f5558b 100644 --- a/interp/testdata/basic.out.ll +++ b/interp/testdata/basic.out.ll @@ -7,6 +7,7 @@ target triple = "x86_64--linux" @main.exportedValue = global [1 x i16*] [i16* @main.exposedValue1] @main.exposedValue1 = global i16 0 @main.exposedValue2 = local_unnamed_addr global i16 0 +@main.insertedValue = local_unnamed_addr global { i8, i32, { float, { i64, i16 } } } zeroinitializer declare void @runtime.printint64(i64) unnamed_addr @@ -27,6 +28,17 @@ entry: store i16 7, i16* @main.exposedValue2 call void @runtime.printint64(i64 6) call void @runtime.printint64(i64 -1) + %agg = call { i8, i32, { float, { i64, i16 } } } @nestedStruct() + %elt.agg = extractvalue { i8, i32, { float, { i64, i16 } } } %agg, 2 + %elt.agg1 = extractvalue { float, { i64, i16 } } %elt.agg, 1 + %elt = extractvalue { i64, i16 } %elt.agg1, 0 + call void @runtime.printint64(i64 %elt) + %agg2.agg0 = extractvalue { i8, i32, { float, { i64, i16 } } } %agg, 2 + %agg2.agg1 = extractvalue { float, { i64, i16 } } %agg2.agg0, 1 + %agg2.insertvalue2 = insertvalue { i64, i16 } %agg2.agg1, i64 5, 0 + %agg2.insertvalue1 = insertvalue { float, { i64, i16 } } %agg2.agg0, { i64, i16 } %agg2.insertvalue2, 1 + %agg2.insertvalue0 = insertvalue { i8, i32, { float, { i64, i16 } } } %agg, { float, { i64, i16 } } %agg2.insertvalue1, 2 + store { i8, i32, { float, { i64, i16 } } } %agg2.insertvalue0, { i8, i32, { float, { i64, i16 } } }* @main.insertedValue ret void } @@ -67,3 +79,5 @@ two: ; preds = %entry otherwise: ; preds = %entry ret i64 -1 } + +declare { i8, i32, { float, { i64, i16 } } } @nestedStruct() local_unnamed_addr