interp: add testing for scanning for side effects
Этот коммит содержится в:
родитель
071f863e5d
коммит
923a6f5873
3 изменённых файлов: 155 добавлений и 0 удалений
|
@ -6,6 +6,21 @@ import (
|
|||
|
||||
type sideEffectSeverity int
|
||||
|
||||
func (severity sideEffectSeverity) String() string {
|
||||
switch severity {
|
||||
case sideEffectInProgress:
|
||||
return "in progress"
|
||||
case sideEffectNone:
|
||||
return "none"
|
||||
case sideEffectLimited:
|
||||
return "limited"
|
||||
case sideEffectAll:
|
||||
return "all"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
sideEffectInProgress sideEffectSeverity = iota // computing side effects is in progress (for recursive functions)
|
||||
sideEffectNone // no side effects at all (pure)
|
||||
|
|
87
interp/scan_test.go
Обычный файл
87
interp/scan_test.go
Обычный файл
|
@ -0,0 +1,87 @@
|
|||
package interp
|
||||
|
||||
import (
|
||||
"os"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
var scanTestTable = []struct {
|
||||
name string
|
||||
severity sideEffectSeverity
|
||||
mentionsGlobals []string
|
||||
}{
|
||||
{"returnsConst", sideEffectNone, nil},
|
||||
{"returnsArg", sideEffectNone, nil},
|
||||
{"externalCallOnly", sideEffectNone, nil},
|
||||
{"externalCallAndReturn", sideEffectLimited, nil},
|
||||
{"externalCallBranch", sideEffectLimited, nil},
|
||||
{"readCleanGlobal", sideEffectNone, []string{"cleanGlobalInt"}},
|
||||
{"readDirtyGlobal", sideEffectLimited, []string{"dirtyGlobalInt"}},
|
||||
{"callFunctionPointer", sideEffectAll, []string{"functionPointer"}},
|
||||
}
|
||||
|
||||
func TestScan(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Read the input IR.
|
||||
path := "testdata/scan.ll"
|
||||
ctx := llvm.NewContext()
|
||||
buf, err := llvm.NewMemoryBufferFromFile(path)
|
||||
os.Stat(path) // make sure this file is tracked by `go test` caching
|
||||
if err != nil {
|
||||
t.Fatalf("could not read file %s: %v", path, err)
|
||||
}
|
||||
mod, err := ctx.ParseIR(buf)
|
||||
if err != nil {
|
||||
t.Fatalf("could not load module:\n%v", err)
|
||||
}
|
||||
|
||||
// Check all to-be-tested functions.
|
||||
for _, tc := range scanTestTable {
|
||||
// Create an eval object, for testing.
|
||||
e := &Eval{
|
||||
Mod: mod,
|
||||
TargetData: llvm.NewTargetData(mod.DataLayout()),
|
||||
dirtyGlobals: map[llvm.Value]struct{}{},
|
||||
}
|
||||
|
||||
// Mark some globals dirty, for testing.
|
||||
e.markDirty(mod.NamedGlobal("dirtyGlobalInt"))
|
||||
|
||||
// Scan for side effects.
|
||||
fn := mod.NamedFunction(tc.name)
|
||||
if fn.IsNil() {
|
||||
t.Errorf("scan test: could not find tested function %s in the IR", tc.name)
|
||||
continue
|
||||
}
|
||||
result := e.hasSideEffects(fn)
|
||||
|
||||
// Check whether the result is what we expect.
|
||||
if result.severity != tc.severity {
|
||||
t.Errorf("scan test: function %s should have severity %s but it has %s", tc.name, tc.severity, result.severity)
|
||||
}
|
||||
|
||||
// Check whether the mentioned globals match with what we'd expect.
|
||||
mentionsGlobalNames := make([]string, 0, len(result.mentionsGlobals))
|
||||
for global := range result.mentionsGlobals {
|
||||
mentionsGlobalNames = append(mentionsGlobalNames, global.Name())
|
||||
}
|
||||
sort.Strings(mentionsGlobalNames)
|
||||
globalsMismatch := false
|
||||
if len(result.mentionsGlobals) != len(tc.mentionsGlobals) {
|
||||
globalsMismatch = true
|
||||
} else {
|
||||
for i, globalName := range mentionsGlobalNames {
|
||||
if tc.mentionsGlobals[i] != globalName {
|
||||
globalsMismatch = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if globalsMismatch {
|
||||
t.Errorf("scan test: expected %s to mention globals %v, but it mentions globals %v", tc.name, tc.mentionsGlobals, mentionsGlobalNames)
|
||||
}
|
||||
}
|
||||
}
|
53
interp/testdata/scan.ll
предоставленный
Обычный файл
53
interp/testdata/scan.ll
предоставленный
Обычный файл
|
@ -0,0 +1,53 @@
|
|||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64--linux"
|
||||
|
||||
define i64 @returnsConst() {
|
||||
ret i64 0
|
||||
}
|
||||
|
||||
define i64 @returnsArg(i64 %arg) {
|
||||
ret i64 %arg
|
||||
}
|
||||
|
||||
declare i64 @externalCall()
|
||||
|
||||
define i64 @externalCallOnly() {
|
||||
%result = call i64 @externalCall()
|
||||
ret i64 0
|
||||
}
|
||||
|
||||
define i64 @externalCallAndReturn() {
|
||||
%result = call i64 @externalCall()
|
||||
ret i64 %result
|
||||
}
|
||||
|
||||
define i64 @externalCallBranch() {
|
||||
%result = call i64 @externalCall()
|
||||
%zero = icmp eq i64 %result, 0
|
||||
br i1 %zero, label %if.then, label %if.done
|
||||
|
||||
if.then:
|
||||
ret i64 2
|
||||
|
||||
if.done:
|
||||
ret i64 4
|
||||
}
|
||||
|
||||
@cleanGlobalInt = global i64 5
|
||||
define i64 @readCleanGlobal() {
|
||||
%global = load i64, i64* @cleanGlobalInt
|
||||
ret i64 %global
|
||||
}
|
||||
|
||||
@dirtyGlobalInt = global i64 5
|
||||
define i64 @readDirtyGlobal() {
|
||||
%global = load i64, i64* @dirtyGlobalInt
|
||||
ret i64 %global
|
||||
}
|
||||
|
||||
@functionPointer = global i64()* null
|
||||
define i64 @callFunctionPointer() {
|
||||
%fp = load i64()*, i64()** @functionPointer
|
||||
%result = call i64 %fp()
|
||||
ret i64 %result
|
||||
}
|
Загрузка…
Создание таблицы
Сослаться в новой задаче