asm

package
v1.0.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 28, 2023 License: Apache-2.0 Imports: 5 Imported by: 0

Documentation

Index

Constants

View Source
const JumpTableMaximumOffset = math.MaxUint32

JumpTableMaximumOffset represents the limit on the size of jump table in bytes. When users try loading an extremely large WebAssembly binary which contains a br_table statement with approximately 4294967296 (2^32) targets. Realistically speaking, that kind of binary could result in more than ten gigabytes of native compiled code where we have to care about huge stacks whose height might exceed 32-bit range, and such huge stack doesn't work with the current implementation.

Variables

This section is empty.

Functions

This section is empty.

Types

type AssemblerBase

type AssemblerBase interface {
	// Reset resets the state of Assembler implementation and mark it ready for
	// the compilation of the new function compilation.
	Reset()

	// Assemble produces the final binary for the assembled operations.
	Assemble(Buffer) error

	// SetJumpTargetOnNext instructs the assembler that the next node must be
	// assigned to the given node's jump destination.
	SetJumpTargetOnNext(node Node)

	// BuildJumpTable calculates the offsets between the first instruction `initialInstructions[0]`
	// and others (e.g. initialInstructions[3]), and wrote the calculated offsets into pre-allocated
	// `table` StaticConst in little endian.
	BuildJumpTable(table *StaticConst, initialInstructions []Node)

	// AllocateNOP allocates Node for NOP instruction.
	AllocateNOP() Node

	// Add appends the given `Node` in the assembled linked list.
	Add(Node)

	// CompileStandAlone adds an instruction to take no arguments.
	CompileStandAlone(instruction Instruction) Node

	// CompileConstToRegister adds an instruction where source operand is `value` as constant and destination is `destinationReg` register.
	CompileConstToRegister(instruction Instruction, value ConstantValue, destinationReg Register) Node

	// CompileRegisterToRegister adds an instruction where source and destination operands are registers.
	CompileRegisterToRegister(instruction Instruction, from, to Register)

	// CompileMemoryToRegister adds an instruction where source operands is the memory address specified by `sourceBaseReg+sourceOffsetConst`
	// and the destination is `destinationReg` register.
	CompileMemoryToRegister(
		instruction Instruction,
		sourceBaseReg Register,
		sourceOffsetConst ConstantValue,
		destinationReg Register,
	)

	// CompileRegisterToMemory adds an instruction where source operand is `sourceRegister` register and the destination is the
	// memory address specified by `destinationBaseRegister+destinationOffsetConst`.
	CompileRegisterToMemory(
		instruction Instruction,
		sourceRegister Register,
		destinationBaseRegister Register,
		destinationOffsetConst ConstantValue,
	)

	// CompileJump adds jump-type instruction and returns the corresponding Node in the assembled linked list.
	CompileJump(jmpInstruction Instruction) Node

	// CompileJumpToRegister adds jump-type instruction whose destination is the memory address specified by `reg` register.
	CompileJumpToRegister(jmpInstruction Instruction, reg Register)

	// CompileReadInstructionAddress adds an ADR instruction to set the absolute address of "target instruction"
	// into destinationRegister. "target instruction" is specified by beforeTargetInst argument and
	// the target is determined by "the instruction right after beforeTargetInst type".
	//
	// For example, if `beforeTargetInst == RET` and we have the instruction sequence like
	// `ADR -> X -> Y -> ... -> RET -> MOV`, then the `ADR` instruction emitted by this function set the absolute
	// address of `MOV` instruction into the destination register.
	CompileReadInstructionAddress(destinationRegister Register, beforeAcquisitionTargetInstruction Instruction)
}

AssemblerBase is the common interface for assemblers among multiple architectures.

Note: some of them can be implemented in an arch-independent way, but not all can be implemented as such. However, we intentionally put such arch-dependant methods here in order to provide the common documentation interface.

type BaseAssemblerImpl

type BaseAssemblerImpl struct {
	// SetBranchTargetOnNextNodes holds branch kind instructions (BR, conditional BR, etc.)
	// where we want to set the next coming instruction as the destination of these BR instructions.
	SetBranchTargetOnNextNodes []Node

	// JumpTableEntries holds the information to build jump tables.
	JumpTableEntries []JumpTableEntry
}

BaseAssemblerImpl includes code common to all architectures.

Note: When possible, add code here instead of in architecture-specific files to reduce drift: As this is internal, exporting symbols only to reduce duplication is ok.

func (*BaseAssemblerImpl) BuildJumpTable

func (a *BaseAssemblerImpl) BuildJumpTable(table *StaticConst, labelInitialInstructions []Node)

BuildJumpTable implements AssemblerBase.BuildJumpTable

func (*BaseAssemblerImpl) FinalizeJumpTableEntry

func (a *BaseAssemblerImpl) FinalizeJumpTableEntry(code []byte) (err error)

FinalizeJumpTableEntry finalizes the build tables inside the given code.

func (*BaseAssemblerImpl) SetJumpTargetOnNext

func (a *BaseAssemblerImpl) SetJumpTargetOnNext(node Node)

SetJumpTargetOnNext implements AssemblerBase.SetJumpTargetOnNext

type Buffer

type Buffer struct {
	*CodeSegment
	// contains filtered or unexported fields
}

Buffer is a reference type representing a section beginning at the end of a code segment where new instructions can be written.

func (Buffer) Append4Bytes

func (buf Buffer) Append4Bytes(a, b, c, d byte)

func (Buffer) Bytes

func (buf Buffer) Bytes() []byte

func (Buffer) Cap

func (buf Buffer) Cap() int

func (Buffer) Len

func (buf Buffer) Len() int

func (Buffer) Reset

func (buf Buffer) Reset()

func (Buffer) Truncate

func (buf Buffer) Truncate(n int)

type CodeSegment

type CodeSegment struct {
	// contains filtered or unexported fields
}

CodeSegment represents a memory mapped segment where native CPU instructions are written.

To construct code segments, the program must call Next to obtain a buffer view capable of writing data at the end of the segment. Next must be called before generating the code of a function because it aligns the next write on 16 bytes.

Instances of CodeSegment hold references to memory which is NOT managed by the garbage collector and therefore must be released *manually* by calling their Unmap method to prevent memory leaks.

The zero value is a valid, empty code segment, equivalent to being constructed by calling NewCodeSegment(nil).

func NewCodeSegment

func NewCodeSegment(code []byte) *CodeSegment

NewCodeSegment constructs a CodeSegment value from a byte slice.

No validation is made that the byte slice is a memory mapped region which can be unmapped on Close.

func (*CodeSegment) Addr

func (seg *CodeSegment) Addr() uintptr

Addr returns the address of the beginning of the code segment as a uintptr.

func (*CodeSegment) Append

func (seg *CodeSegment) Append(n int) []byte

Append appends n bytes to the code segment, returning a slice to the appended memory region.

The underlying code segment may be reallocated if it was too short to hold n more bytes, which invalidates any addresses previously returned by calls to Addr.

func (*CodeSegment) AppendByte

func (seg *CodeSegment) AppendByte(b byte)

AppendByte appends a single byte to the code segment.

The underlying code segment may be reallocated if it was too short to hold one more byte, which invalidates any addresses previously returned by calls to Addr.

func (*CodeSegment) AppendBytes

func (seg *CodeSegment) AppendBytes(b []byte)

AppendBytes appends a copy of b to the code segment.

The underlying code segment may be reallocated if it was too short to hold len(b) more bytes, which invalidates any addresses previously returned by calls to Addr.

func (*CodeSegment) AppendUint32

func (seg *CodeSegment) AppendUint32(u uint32)

AppendUint32 appends a 32 bits integer to the code segment.

The underlying code segment may be reallocated if it was too short to hold four more bytes, which invalidates any addresses previously returned by calls to Addr.

func (*CodeSegment) Bytes

func (seg *CodeSegment) Bytes() []byte

Bytes returns a byte slice to the memory mapping of the code segment.

The returned slice remains valid until more bytes are written to a buffer of the code segment, or Unmap is called.

func (*CodeSegment) Grow

func (seg *CodeSegment) Grow(n int)

Grow ensure that the capacity of the code segment is large enough to hold n more bytes.

The underlying code segment may be reallocated if it was too short, which invalidates any addresses previously returned by calls to Addr.

func (*CodeSegment) Len

func (seg *CodeSegment) Len() int

Len returns the length of the byte slice referencing the memory mapping of the code segment.

func (*CodeSegment) Map

func (seg *CodeSegment) Map(size int) error

Map allocates a memory mapping of the given size to the code segment.

Note that programs only need to use this method to initialize the code segment to a specific content (e.g. when loading pre-compiled code from a file), otherwise the backing memory mapping is allocated on demand when code is written to the code segment via Buffers returned by calls to Next.

The method errors is the segment is already backed by a memory mapping.

func (*CodeSegment) NextCodeSection

func (seg *CodeSegment) NextCodeSection() Buffer

Next returns a buffer pointed at the end of the code segment to support writing more code instructions to it.

Buffers are passed by value, but they hold a reference to the code segment that they were created from.

func (*CodeSegment) Size

func (seg *CodeSegment) Size() uintptr

Size returns the size of code segment, which is less or equal to the length of the byte slice returned by Len or Bytes.

func (*CodeSegment) Unmap

func (seg *CodeSegment) Unmap() error

Close unmaps the underlying memory region held by the code segment, clearing its state back to an empty code segment.

The value is still usable after unmapping its memory, a new memory area can be allocated by calling Map or writing to the segment.

type ConditionalRegisterState

type ConditionalRegisterState byte

ConditionalRegisterState represents architecture-specific conditional register's states.

const ConditionalRegisterStateUnset ConditionalRegisterState = 0

ConditionalRegisterStateUnset is the only architecture-independent conditional state, and can be used to indicate that no conditional state is specified.

type ConstantValue

type ConstantValue = int64

ConstantValue represents a constant value used in an instruction.

type Instruction

type Instruction uint16 // to accommodate the high cardinality of vector ops

Instruction represents architecture-specific instructions.

type JumpTableEntry

type JumpTableEntry struct {
	// contains filtered or unexported fields
}

JumpTableEntry is the necessary data to build a jump table. This is exported for testing purpose.

type Node

type Node interface {
	fmt.Stringer

	// AssignJumpTarget assigns the given target node as the destination of
	// jump instruction for this Node.
	AssignJumpTarget(target Node)

	// AssignDestinationConstant assigns the given constant as the destination
	// of the instruction for this node.
	AssignDestinationConstant(value ConstantValue)

	// AssignSourceConstant assigns the given constant as the source
	// of the instruction for this node.
	AssignSourceConstant(value ConstantValue)

	// OffsetInBinary returns the offset of this node in the assembled binary.
	OffsetInBinary() NodeOffsetInBinary
}

Node represents a node in the linked list of assembled operations.

type NodeOffsetInBinary

type NodeOffsetInBinary = uint64

NodeOffsetInBinary represents an offset of this node in the final binary.

type Register

type Register byte

Register represents architecture-specific registers.

const NilRegister Register = 0

NilRegister is the only architecture-independent register, and can be used to indicate that no register is specified.

type StaticConst

type StaticConst struct {
	Raw []byte
	// OffsetInBinary is the offset of this static const in the result binary.
	OffsetInBinary uint64
	// contains filtered or unexported fields
}

StaticConst represents an arbitrary constant bytes which are pooled and emitted by assembler into the binary. These constants can be referenced by instructions.

func NewStaticConst

func NewStaticConst(raw []byte) *StaticConst

NewStaticConst returns the pointer to the new NewStaticConst for given bytes.

func (*StaticConst) AddOffsetFinalizedCallback

func (s *StaticConst) AddOffsetFinalizedCallback(cb func(offsetOfConstInBinary uint64))

AddOffsetFinalizedCallback adds a callback into offsetFinalizedCallbacks.

func (*StaticConst) SetOffsetInBinary

func (s *StaticConst) SetOffsetInBinary(offset uint64)

SetOffsetInBinary finalizes the offset of this StaticConst, and invokes callbacks.

type StaticConstPool

type StaticConstPool struct {
	Consts []*StaticConst
	// FirstUseOffsetInBinary holds the offset of the first instruction which accesses this const pool .
	FirstUseOffsetInBinary NodeOffsetInBinary
	// PoolSizeInBytes is the current size of the pool in bytes.
	PoolSizeInBytes int
	// contains filtered or unexported fields
}

StaticConstPool holds a bulk of StaticConst which are yet to be emitted into the binary.

func NewStaticConstPool

func NewStaticConstPool() StaticConstPool

func (*StaticConstPool) AddConst

func (p *StaticConstPool) AddConst(c *StaticConst, useOffset NodeOffsetInBinary)

AddConst adds a *StaticConst into the pool if it's not already added.

func (*StaticConstPool) Empty

func (p *StaticConstPool) Empty() bool

Empty returns true if StaticConstPool is empty.

func (*StaticConstPool) Reset

func (p *StaticConstPool) Reset()

Reset resets the *StaticConstPool for reuse.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL