aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2021-05-02 17:39:52 +0200
committerRon Evans <[email protected]>2021-05-04 21:21:56 +0200
commit944f0220602bb6a30af8f3b894499e51a69517ed (patch)
tree418305ff9e05985aa902b3eec2a7713258d9edda
parentcd517a30afd608dc6dd52745690c6dbd4f7fb010 (diff)
downloadtinygo-944f0220602bb6a30af8f3b894499e51a69517ed.tar.gz
tinygo-944f0220602bb6a30af8f3b894499e51a69517ed.zip
interp: support extractvalue/insertvalue with multiple operands
TinyGo doesn't emit these instructions, but they can occur as a result of optimizations.
-rw-r--r--interp/interpreter.go28
-rw-r--r--interp/testdata/basic.ll10
-rw-r--r--interp/testdata/basic.out.ll14
3 files changed, 46 insertions, 6 deletions
diff --git a/interp/interpreter.go b/interp/interpreter.go
index ec71c56e0..c6ce6b7f9 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 e3ff974e2..001170582 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
[email protected] = 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 e873b1c8a..24f5558ba 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
[email protected] = 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