| /* |
| * Copyright 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/memory" |
| "android.googlesource.com/platform/tools/gpu/replay/opcode" |
| "android.googlesource.com/platform/tools/gpu/replay/value" |
| "android.googlesource.com/platform/tools/gpu/replay/vm" |
| ) |
| |
| // 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 vm.Type, v uint64, e *binary.Encoder) error { |
| mask19 := uint64(0x7ffff) |
| mask20 := uint64(0xfffff) |
| mask26 := uint64(0x3ffffff) |
| mask45 := uint64(0x1fffffffffff) |
| mask46 := uint64(0x3fffffffffff) |
| |
| // ▏60 ▏50 ▏40 ▏30 ▏20 ▏10 |
| // ○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○●●●●●●●●●●●●●●●●●●● mask19 |
| // ○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○●●●●●●●●●●●●●●●●●●●● mask20 |
| // ○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○●●●●●●●●●●●●●●●●●●●●●●●●●● mask26 |
| // ○○○○○○○○○○○○○○○○○○○●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●● mask45 |
| // ○○○○○○○○○○○○○○○○○○●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●● mask46 |
| // ▕ PUSHI 20 ▕ |
| // ▕ EXTEND 26 ▕ |
| |
| switch t { |
| case vm.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) |
| } |
| case vm.TypeDouble: |
| push := opcode.PushI{DataType: t, Value: uint32(v >> 52)} |
| if err := push.Encode(e); err != nil { |
| return err |
| } |
| if v&0xfffffffffffff != 0 { |
| ext := opcode.Extend{Value: uint32(v >> 26)} |
| if err := ext.Encode(e); err != nil { |
| return err |
| } |
| return opcode.Extend{Value: uint32(v)}.Encode(e) |
| } |
| case vm.TypeInt8, vm.TypeInt16, vm.TypeInt32, vm.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 vm.TypeBool, |
| vm.TypeUint8, vm.TypeUint16, vm.TypeUint32, vm.TypeUint64, |
| vm.TypeAbsolutePointer, vm.TypeConstantPointer, vm.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.String()) |
| } |
| |
| // 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 { |
| return encodePush(a.Value.Type(), a.Value.Get(r), 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 source address and then the target |
| // 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 vm.Type |
| Source value.Pointer |
| } |
| |
| func (a Load) Encode(r value.PointerResolver, e *binary.Encoder) error { |
| addr := a.Source.Get(r) |
| switch a.Source.(type) { |
| case value.ConstantPointer: |
| if addr < 0x100000 { |
| return opcode.LoadC{DataType: a.DataType, Address: uint32(addr)}.Encode(e) |
| } else { |
| if err := encodePush(a.Source.Type(), a.Source.Get(r), e); err != nil { |
| return err |
| } |
| return opcode.Load{DataType: a.DataType}.Encode(e) |
| } |
| case value.VolatilePointer, value.VolatileTemporaryPointer, value.VolatileCapturePointer: |
| if addr < 0x100000 { |
| return opcode.LoadV{DataType: a.DataType, Address: uint32(addr)}.Encode(e) |
| } else { |
| if err := encodePush(a.Source.Type(), a.Source.Get(r), e); err != nil { |
| return err |
| } |
| return opcode.Load{DataType: a.DataType}.Encode(e) |
| } |
| default: |
| return fmt.Errorf("Unsupported load source type %T", a.Source) |
| } |
| } |
| |
| // 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 { |
| addr := a.Destination.Get(r) |
| if addr < 0x3ffffff { |
| return opcode.StoreV{Address: uint32(addr)}.Encode(e) |
| } else { |
| if err := encodePush(a.Destination.Type(), a.Destination.Get(r), e); err != nil { |
| return err |
| } |
| return opcode.Store{}.Encode(e) |
| } |
| } |
| |
| // Strcpy is an Instruction that pops the source address then the target 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 memory.Pointer |
| } |
| |
| func (a Resource) Encode(r value.PointerResolver, e *binary.Encoder) error { |
| ptr := value.VolatileCapturePointer(a.Destination) |
| if err := encodePush(ptr.Type(), ptr.Get(r), 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 { |
| if err := encodePush(a.Source.Type(), a.Source.Get(r), e); err != nil { |
| return err |
| } |
| if err := encodePush(vm.TypeUint32, a.Size, e); err != nil { |
| return err |
| } |
| return opcode.Post{}.Encode(e) |
| } |