blob: 44ed91d37b171919b0e60d753dbd938e868db4e3 [file] [log] [blame]
// Copyright (C) 2016 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package builder
import (
"errors"
"testing"
"android.googlesource.com/platform/tools/gpu/framework/assert"
"android.googlesource.com/platform/tools/gpu/framework/binary"
"android.googlesource.com/platform/tools/gpu/framework/device"
"android.googlesource.com/platform/tools/gpu/gapid/memory"
"android.googlesource.com/platform/tools/gpu/gapid/replay/asm"
"android.googlesource.com/platform/tools/gpu/gapid/replay/protocol"
"android.googlesource.com/platform/tools/gpu/gapid/replay/value"
)
func TestCommitAtom(t *testing.T) {
ctx := assert.Context(t)
for _, test := range []struct {
name string
f func(*Builder)
expected []asm.Instruction
}{
{
"Call with used return value",
func(b *Builder) {
b.BeginAtom(10)
b.Push(value.U8(1))
b.Call(FunctionInfo{0, 123, protocol.Type_Uint8, 1})
b.Store(value.AbsolutePointer(0x10000))
b.CommitAtom()
},
[]asm.Instruction{
asm.Label{Value: 10},
asm.Push{Value: value.U8(1)},
asm.Call{PushReturn: true, ApiIndex: 0, FunctionID: 123},
asm.Store{Destination: value.AbsolutePointer(0x10000)},
},
},
{
"Call with unused return value",
func(b *Builder) {
b.BeginAtom(10)
b.Push(value.U8(1))
b.Call(FunctionInfo{1, 123, protocol.Type_Uint8, 1})
b.CommitAtom()
},
[]asm.Instruction{
asm.Label{Value: 10},
asm.Push{Value: value.U8(1)},
asm.Call{PushReturn: false, ApiIndex: 1, FunctionID: 123},
},
},
{
"Remove unused push",
func(b *Builder) {
b.BeginAtom(10)
b.Push(value.U32(12))
b.CommitAtom()
},
[]asm.Instruction{
asm.Label{Value: 10},
},
},
{
"Unused pushes",
func(b *Builder) {
b.BeginAtom(10)
b.Push(value.U32(12))
b.Push(value.U32(34))
b.Push(value.U32(56))
b.CommitAtom()
},
[]asm.Instruction{
asm.Label{Value: 10},
},
},
{
"Unused clone",
func(b *Builder) {
b.BeginAtom(10)
b.Call(FunctionInfo{0, 123, protocol.Type_Uint8, 0})
b.Clone(0)
b.CommitAtom()
},
[]asm.Instruction{
asm.Label{Value: 10},
asm.Call{PushReturn: true, ApiIndex: 0, FunctionID: 123},
asm.Pop{Count: 1},
},
},
{
"Unused clone",
func(b *Builder) {
b.BeginAtom(10)
b.Call(FunctionInfo{1, 123, protocol.Type_Uint8, 0})
b.Clone(0)
b.Store(value.AbsolutePointer(0x10000))
b.CommitAtom()
},
[]asm.Instruction{
asm.Label{Value: 10},
asm.Call{PushReturn: true, ApiIndex: 1, FunctionID: 123},
asm.Nop{},
asm.Store{Destination: value.AbsolutePointer(0x10000)},
},
},
{
"Unused clone of return value",
func(b *Builder) {
b.BeginAtom(10)
b.Call(FunctionInfo{0, 123, protocol.Type_Uint8, 0})
b.Clone(0)
b.CommitAtom()
},
[]asm.Instruction{
asm.Label{Value: 10},
// TODO: Could be optimised further.
asm.Call{PushReturn: true, ApiIndex: 0, FunctionID: 123},
asm.Pop{Count: 1},
},
},
{
"Use one of three return values",
func(b *Builder) {
b.BeginAtom(10)
b.Call(FunctionInfo{0, 123, protocol.Type_Uint8, 0})
b.Call(FunctionInfo{0, 123, protocol.Type_Uint8, 0})
b.Call(FunctionInfo{0, 123, protocol.Type_Uint8, 0})
b.Clone(1)
b.Store(value.AbsolutePointer(0x10000))
b.CommitAtom()
},
[]asm.Instruction{
asm.Label{Value: 10},
asm.Call{PushReturn: false, ApiIndex: 0, FunctionID: 123},
asm.Call{PushReturn: true, ApiIndex: 0, FunctionID: 123},
asm.Call{PushReturn: false, ApiIndex: 0, FunctionID: 123},
asm.Nop{},
asm.Store{Destination: value.AbsolutePointer(0x10000)},
},
},
} {
b := New(device.MemoryLayout{
PointerAlignment: 4,
PointerSize: 4,
IntegerSize: 4,
SizeSize: 4,
U64Alignment: 8,
Endian: device.LittleEndian,
})
test.f(b)
assert.For(ctx, test.name).ThatSlice(b.instructions).Equals(test.expected)
}
}
func TestRevertAtom(t *testing.T) {
ctx := assert.Context(t)
for _, test := range []struct {
name string
f func(*Builder)
expected []asm.Instruction
}{
{
"Revert atom",
func(b *Builder) {
b.BeginAtom(10)
b.Push(value.U8(1))
b.Call(FunctionInfo{1, 123, protocol.Type_Uint8, 1})
b.Store(value.AbsolutePointer(0x10000))
b.RevertAtom(nil)
},
[]asm.Instruction{},
},
{
"Commit atom, revert atom",
func(b *Builder) {
b.BeginAtom(10)
b.Push(value.U8(1))
b.Call(FunctionInfo{1, 123, protocol.Type_Uint8, 1})
b.Store(value.AbsolutePointer(0x10000))
b.CommitAtom()
b.BeginAtom(20)
b.Push(value.U8(2))
b.Call(FunctionInfo{1, 234, protocol.Type_Uint8, 1})
b.Store(value.AbsolutePointer(0x10000))
b.RevertAtom(nil)
},
[]asm.Instruction{
asm.Label{Value: 10},
asm.Push{Value: value.U8(1)},
asm.Call{PushReturn: true, ApiIndex: 1, FunctionID: 123},
asm.Store{Destination: value.AbsolutePointer(0x10000)},
},
},
} {
b := New(device.MemoryLayout{
PointerAlignment: 4,
PointerSize: 4,
IntegerSize: 4,
SizeSize: 4,
U64Alignment: 8,
Endian: device.LittleEndian,
})
test.f(b)
assert.For(ctx, test.name).ThatSlice(b.instructions).Equals(test.expected)
}
}
func TestRevertPostbackAtom(t *testing.T) {
ctx := assert.Context(t)
expectedErr := errors.New("Oh noes!")
postbackErr := error(nil)
postback := Postback(func(r binary.Reader, err error) error {
assert.For(ctx, "Postback reader").That(r).IsNil()
postbackErr = err
return nil
})
for _, test := range []struct {
name string
f func(*Builder)
expected []asm.Instruction
}{
{
"Revert postback atom",
func(b *Builder) {
b.BeginAtom(10)
b.Post(value.AbsolutePointer(0x10000), 100, postback)
b.RevertAtom(expectedErr)
},
[]asm.Instruction{},
},
} {
ctx := ctx.Enter(test.name)
b := New(device.MemoryLayout{
PointerAlignment: 4,
PointerSize: 4,
IntegerSize: 4,
SizeSize: 4,
U64Alignment: 8,
Endian: device.LittleEndian,
})
test.f(b)
assert.With(ctx).ThatSlice(b.instructions).Equals(test.expected)
}
assert.For(ctx, "Postback was not informed of RevertAtom").ThatError(postbackErr).Equals(expectedErr)
}
func TestMapMemory(t *testing.T) {
ctx := assert.Context(t)
for _, test := range []struct {
name string
f func(*Builder)
expected []asm.Instruction
}{
{
"No mapping",
func(b *Builder) {
b.BeginAtom(10)
b.Push(value.ObservedPointer(0x100004))
b.Call(FunctionInfo{0, 123, protocol.Type_VolatilePointer, 1})
b.CommitAtom()
},
[]asm.Instruction{
asm.Label{Value: 10},
asm.Push{Value: value.ObservedPointer(0x100004)},
asm.Call{ApiIndex: 0, FunctionID: 123},
},
},
{
"MapMemory",
func(b *Builder) {
b.BeginAtom(10)
b.Call(FunctionInfo{0, 100, protocol.Type_AbsolutePointer, 0})
b.MapMemory(memory.Range{Base: 0x100000, Size: 0x10})
b.CommitAtom()
b.BeginAtom(20)
b.Push(value.ObservedPointer(0x100004))
b.Call(FunctionInfo{0, 123, protocol.Type_Void, 1})
b.CommitAtom()
},
[]asm.Instruction{
asm.Label{Value: 10},
asm.Call{ApiIndex: 0, FunctionID: 100, PushReturn: true},
asm.Store{Destination: value.VolatilePointer(0x0)}, // mapping-storage address
asm.Label{Value: 20},
asm.Load{DataType: protocol.Type_AbsolutePointer, Source: value.VolatilePointer(0x0)}, // mapping-storage address
asm.Push{Value: value.AbsolutePointer(0x4)},
asm.Add{Count: 2},
asm.Call{ApiIndex: 0, FunctionID: 123},
},
},
{
"UnmapMemory",
func(b *Builder) {
b.BeginAtom(10)
b.Call(FunctionInfo{0, 100, protocol.Type_AbsolutePointer, 0})
b.MapMemory(memory.Range{Base: 0x100000, Size: 0x10})
b.CommitAtom()
b.BeginAtom(20)
b.UnmapMemory(memory.Range{Base: 0x100000, Size: 0x10})
b.CommitAtom()
b.BeginAtom(30)
b.Push(value.ObservedPointer(0x100004))
b.Call(FunctionInfo{0, 123, protocol.Type_Void, 1})
b.CommitAtom()
},
[]asm.Instruction{
asm.Label{Value: 10},
asm.Call{ApiIndex: 0, FunctionID: 100, PushReturn: true},
asm.Store{Destination: value.VolatilePointer(0x0)}, // mapping-storage address
asm.Label{Value: 20},
asm.Label{Value: 30},
asm.Push{Value: value.ObservedPointer(0x100004)},
asm.Call{ApiIndex: 0, FunctionID: 123},
},
},
} {
ctx := ctx.Enter(test.name)
b := New(device.MemoryLayout{
PointerAlignment: 4,
PointerSize: 4,
IntegerSize: 4,
SizeSize: 4,
U64Alignment: 8,
Endian: device.LittleEndian,
})
test.f(b)
assert.With(ctx).ThatSlice(b.instructions).Equals(test.expected)
}
}