blob: bcc62a4af29c3a32382af1788f06caf4866649a7 [file] [log] [blame]
// Copyright (C) 2015 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 asm
import (
"fmt"
"android.googlesource.com/platform/tools/gpu/binary"
"android.googlesource.com/platform/tools/gpu/replay/opcode"
"android.googlesource.com/platform/tools/gpu/replay/protocol"
"android.googlesource.com/platform/tools/gpu/replay/value"
)
// Instruction is the interface of all instruction types.
//
// Encode writes the instruction's opcodes to the binary encoder e, translating
// any pointers to their final, resolved addresses using the PointerResolver r.
// An instruction can produce zero, one or many opcodes.
type Instruction interface {
Encode(r value.PointerResolver, e binary.Encoder) error
}
func encodePush(t protocol.Type, v uint64, e binary.Encoder) error {
mask19 := uint64(0x7ffff)
mask20 := uint64(0xfffff)
mask26 := uint64(0x3ffffff)
mask45 := uint64(0x1fffffffffff)
mask46 := uint64(0x3fffffffffff)
mask52 := uint64(0xfffffffffffff)
// ▏60 ▏50 ▏40 ▏30 ▏20 ▏10
// ○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○●●●●●●●●●●●●●●●●●●● mask19
// ○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○●●●●●●●●●●●●●●●●●●●● mask20
// ○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○●●●●●●●●●●●●●●●●●●●●●●●●●● mask26
// ○○○○○○○○○○○○○○○○○○○●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●● mask45
// ○○○○○○○○○○○○○○○○○○●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●● mask46
// ○○○○○○○○○○○○●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●● mask52
// ▕ PUSHI 20 ▕
// ▕ EXTEND 26 ▕
switch t {
case protocol.TypeFloat:
push := opcode.PushI{DataType: t, Value: uint32(v >> 23)}
if err := push.Encode(e); err != nil {
return err
}
if v&0x7fffff != 0 {
return opcode.Extend{Value: uint32(v & 0x7fffff)}.Encode(e)
}
return nil
case protocol.TypeDouble:
push := opcode.PushI{DataType: t, Value: uint32(v >> 52)}
if err := push.Encode(e); err != nil {
return err
}
v &= mask52
if v != 0 {
ext := opcode.Extend{Value: uint32(v >> 26)}
if err := ext.Encode(e); err != nil {
return err
}
return opcode.Extend{Value: uint32(v & mask26)}.Encode(e)
}
return nil
case protocol.TypeInt8, protocol.TypeInt16, protocol.TypeInt32, protocol.TypeInt64:
// Signed PUSHI types are sign-extended
switch {
case v&^mask19 == 0:
// ○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒
// ▕ PUSHI 20 ▕
return opcode.PushI{DataType: t, Value: uint32(v)}.Encode(e)
case v&^mask19 == ^mask19:
// ●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒
// ▕ PUSHI 20 ▕
return opcode.PushI{DataType: t, Value: uint32(v & mask20)}.Encode(e)
case v&^mask45 == 0:
// ○○○○○○○○○○○○○○○○○○○◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒
// ▕ PUSHI 20 ▕ EXTEND 26 ▕
push := opcode.PushI{DataType: t, Value: uint32(v >> 26)}
if err := push.Encode(e); err != nil {
return err
}
return opcode.Extend{uint32(v & mask26)}.Encode(e)
case v&^mask45 == ^mask45:
// ●●●●●●●●●●●●●●●●●●●◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒
// ▕ PUSHI 20 ▕ EXTEND 26 ▕
push := opcode.PushI{DataType: t, Value: uint32((v >> 26) & mask20)}
if err := push.Encode(e); err != nil {
return err
}
return opcode.Extend{uint32(v & mask26)}.Encode(e)
default:
// ◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒
//▕ PUSHI 12 ▕ EXTEND 26 ▕ EXTEND 26 ▕
push := opcode.PushI{DataType: t, Value: uint32(v >> 52)}
if err := push.Encode(e); err != nil {
return err
}
ext := opcode.Extend{uint32((v >> 26) & mask26)}
if err := ext.Encode(e); err != nil {
return err
}
return opcode.Extend{uint32(v & mask26)}.Encode(e)
}
case protocol.TypeBool,
protocol.TypeUint8, protocol.TypeUint16, protocol.TypeUint32, protocol.TypeUint64,
protocol.TypeAbsolutePointer, protocol.TypeConstantPointer, protocol.TypeVolatilePointer:
switch {
case v&^mask20 == 0:
// ○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒
// ▕ PUSHI 20 ▕
return opcode.PushI{DataType: t, Value: uint32(v)}.Encode(e)
case v&^mask46 == 0:
// ○○○○○○○○○○○○○○○○○○◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒
// ▕ PUSHI 20 ▕ EXTEND 26 ▕
push := opcode.PushI{DataType: t, Value: uint32(v >> 26)}
if err := push.Encode(e); err != nil {
return err
}
return opcode.Extend{uint32(v & mask26)}.Encode(e)
default:
// ◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒◒
//▕ PUSHI 12 ▕ EXTEND 26 ▕ EXTEND 26 ▕
push := opcode.PushI{DataType: t, Value: uint32(v >> 52)}
if err := push.Encode(e); err != nil {
return err
}
ext := opcode.Extend{uint32((v >> 26) & mask26)}
if err := ext.Encode(e); err != nil {
return err
}
return opcode.Extend{uint32(v & mask26)}.Encode(e)
}
}
return fmt.Errorf("Cannot push value type %s", t)
}
// Nop is a no-operation Instruction. Instructions of this type do nothing.
type Nop struct{}
func (Nop) Encode(r value.PointerResolver, e binary.Encoder) error {
return nil
}
// Call is an Instruction to call a VM registered function.
// This instruction will pop the parameters from the VM stack starting with the
// first parameter. If PushReturn is true, then the return value of the function
// call will be pushed to the top of the VM stack.
type Call struct {
PushReturn bool // If true, the return value is pushed to the VM stack.
FunctionID uint16 // The function id registered with the VM to invoke.
}
func (a Call) Encode(r value.PointerResolver, e binary.Encoder) error {
return opcode.Call{
PushReturn: a.PushReturn,
FunctionID: a.FunctionID,
}.Encode(e)
}
// Push is an Instruction to push Value to the top of the VM stack.
type Push struct {
Value value.Value // The value to push on to the VM stack.
}
func (a Push) Encode(r value.PointerResolver, e binary.Encoder) error {
ty, val := a.Value.Get(r)
return encodePush(ty, val, e)
}
// Pop is an Instruction that discards Count values from the top of the VM
// stack.
type Pop struct {
Count uint32 // Number of values to discard from the top of the VM stack.
}
func (a Pop) Encode(r value.PointerResolver, e binary.Encoder) error {
return opcode.Pop{Count: a.Count}.Encode(e)
}
// Copy is an Instruction that pops the target address and then the source
// address from the top of the VM stack, and then copies Count bytes from
// source to target.
type Copy struct {
Count uint64 // Number of bytes to copy.
}
func (a Copy) Encode(r value.PointerResolver, e binary.Encoder) error {
return opcode.Copy{Count: uint32(a.Count)}.Encode(e)
}
// Clone is an Instruction that makes a copy of the the n-th element from the
// top of the VM stack and pushes the copy to the top of the VM stack.
type Clone struct {
Index int
}
func (a Clone) Encode(r value.PointerResolver, e binary.Encoder) error {
return opcode.Clone{Index: uint32(a.Index)}.Encode(e)
}
// Load is an Instruction that loads the value of type DataType from pointer
// Source and pushes the loaded value to the top of the VM stack.
type Load struct {
DataType protocol.Type
Source value.Pointer
}
func (a Load) Encode(r value.PointerResolver, e binary.Encoder) error {
ty, addr := a.Source.Get(r)
switch ty {
case protocol.TypeConstantPointer:
if addr < 0x100000 {
return opcode.LoadC{DataType: a.DataType, Address: uint32(addr)}.Encode(e)
}
case protocol.TypeVolatilePointer:
if addr < 0x100000 {
return opcode.LoadV{DataType: a.DataType, Address: uint32(addr)}.Encode(e)
}
default:
return fmt.Errorf("Unsupported load source type %T", a.Source)
}
if err := encodePush(ty, addr, e); err != nil {
return err
}
return opcode.Load{DataType: a.DataType}.Encode(e)
}
// Store is an Instruction that pops the value from the top of the VM stack and
// writes the value to Destination.
type Store struct {
Destination value.Pointer
}
func (a Store) Encode(r value.PointerResolver, e binary.Encoder) error {
ty, addr := a.Destination.Get(r)
if addr < 0x3ffffff {
return opcode.StoreV{Address: uint32(addr)}.Encode(e)
} else {
if err := encodePush(ty, addr, e); err != nil {
return err
}
return opcode.Store{}.Encode(e)
}
}
// Strcpy is an Instruction that pops the target address then the source address
// from the top of the VM stack, and then copies at most MaxCount-1 bytes from
// source to target. If the MaxCount is greater than the source string length,
// then the target will be padded with 0s. The destination buffer will always be
// 0-terminated.
type Strcpy struct {
MaxCount uint64
}
func (a Strcpy) Encode(r value.PointerResolver, e binary.Encoder) error {
return opcode.Strcpy{
MaxSize: uint32(a.MaxCount),
}.Encode(e)
}
// Resource is an Instruction that loads the resource with index Index of Size
// bytes and writes the resource to Destination.
type Resource struct {
Index uint32
Destination uint64
}
func (a Resource) Encode(r value.PointerResolver, e binary.Encoder) error {
ptr := value.RemappedPointer(a.Destination)
ty, val := ptr.Get(r)
if err := encodePush(ty, val, e); err != nil {
return err
}
return opcode.Resource{
ID: a.Index,
}.Encode(e)
}
// Post is an Instruction that posts Size bytes from Source to the server.
type Post struct {
Source value.Pointer
Size uint64
}
func (a Post) Encode(r value.PointerResolver, e binary.Encoder) error {
ty, val := a.Source.Get(r)
if err := encodePush(ty, val, e); err != nil {
return err
}
if err := encodePush(protocol.TypeUint32, a.Size, e); err != nil {
return err
}
return opcode.Post{}.Encode(e)
}
// Label is an Instruction that holds a marker value, used for debugging.
type Label struct {
Value uint32
}
func (a Label) Encode(r value.PointerResolver, e binary.Encoder) error {
return opcode.Label{Value: a.Value}.Encode(e)
}