
These two passes are related, but can definitely work independently. Which is what this change does: it splits the two passes. This should make it easier to change these two new passes in the future. This change now also enables slightly better testing by testing these two passes independently. In particular, the reflect lowering pass got some actual tests: it was barely unit-tested before. I have verified that this doesn't really change code size, at least not on the microbit target. Two tests do change, but in a very minor way (and in opposite direction).
77 строки
2,3 КиБ
Go
77 строки
2,3 КиБ
Go
package transform_test
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/tinygo-org/tinygo/transform"
|
|
"tinygo.org/x/go-llvm"
|
|
)
|
|
|
|
type reflectAssert struct {
|
|
call llvm.Value
|
|
name string
|
|
expectedNumber uint64
|
|
}
|
|
|
|
// Test reflect lowering. This code looks at IR like this:
|
|
//
|
|
// call void @main.assertType(i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:basic:int" to i32), i8* inttoptr (i32 3 to i8*), i32 4, i8* undef, i8* undef)
|
|
//
|
|
// and verifies that the ptrtoint constant (the first parameter of
|
|
// @main.assertType) is replaced with the correct type code. The expected
|
|
// output is this:
|
|
//
|
|
// call void @main.assertType(i32 4, i8* inttoptr (i32 3 to i8*), i32 4, i8* undef, i8* undef)
|
|
//
|
|
// The first and third parameter are compared and must match, the second
|
|
// parameter is ignored.
|
|
func TestReflect(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
mod := compileGoFileForTesting(t, "./testdata/reflect.go")
|
|
|
|
// Run the instcombine pass, to clean up the IR a bit (especially
|
|
// insertvalue/extractvalue instructions).
|
|
pm := llvm.NewPassManager()
|
|
defer pm.Dispose()
|
|
pm.AddInstructionCombiningPass()
|
|
pm.Run(mod)
|
|
|
|
// Get a list of all the asserts in the source code.
|
|
assertType := mod.NamedFunction("main.assertType")
|
|
var asserts []reflectAssert
|
|
for user := assertType.FirstUse(); !user.IsNil(); user = user.NextUse() {
|
|
use := user.User()
|
|
if use.IsACallInst().IsNil() {
|
|
t.Fatal("expected call use of main.assertType")
|
|
}
|
|
global := use.Operand(0).Operand(0)
|
|
expectedNumber := use.Operand(2).ZExtValue()
|
|
asserts = append(asserts, reflectAssert{
|
|
call: use,
|
|
name: global.Name(),
|
|
expectedNumber: expectedNumber,
|
|
})
|
|
}
|
|
|
|
// Sanity check to show that the test is actually testing anything.
|
|
if len(asserts) < 3 {
|
|
t.Errorf("expected at least 3 test cases, got %d", len(asserts))
|
|
}
|
|
|
|
// Now lower the type codes.
|
|
transform.LowerReflect(mod)
|
|
|
|
// Check whether the values are as expected.
|
|
for _, assert := range asserts {
|
|
actualNumberValue := assert.call.Operand(0)
|
|
if actualNumberValue.IsAConstantInt().IsNil() {
|
|
t.Errorf("expected to see a constant for %s, got something else", assert.name)
|
|
continue
|
|
}
|
|
actualNumber := actualNumberValue.ZExtValue()
|
|
if actualNumber != assert.expectedNumber {
|
|
t.Errorf("%s: expected number 0b%b, got 0b%b", assert.name, assert.expectedNumber, actualNumber)
|
|
}
|
|
}
|
|
}
|