interp: add testing for scanning for side effects
Этот коммит содержится в:
родитель
071f863e5d
коммит
923a6f5873
3 изменённых файлов: 155 добавлений и 0 удалений
|
@ -6,6 +6,21 @@ import (
|
||||||
|
|
||||||
type sideEffectSeverity int
|
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 (
|
const (
|
||||||
sideEffectInProgress sideEffectSeverity = iota // computing side effects is in progress (for recursive functions)
|
sideEffectInProgress sideEffectSeverity = iota // computing side effects is in progress (for recursive functions)
|
||||||
sideEffectNone // no side effects at all (pure)
|
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
|
||||||
|
}
|
Загрузка…
Создание таблицы
Сослаться в новой задаче