blob: f290473b63138b57b262b5f8f29c7d16cc9b0c48 [file] [log] [blame]
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:typed_data';
import 'package:native_compiler/back_end/locations.dart';
import 'package:native_compiler/back_end/object_pool.dart';
import 'package:native_compiler/runtime/vm_defs.dart';
import 'package:cfg/ir/constant_value.dart' show ConstantValue;
enum OperandSize {
u8,
u16,
u32,
u64,
s8,
s16,
s32,
s64;
bool get is32 => (this == u32) || (this == s32);
bool get is64 => (this == u64) || (this == s64);
bool get is32or64 => is32 || is64;
bool get isSigned =>
(this == s8) || (this == s16) || (this == s32) || (this == s64);
int get bitWidth => switch (this) {
u8 || s8 => 8,
u16 || s16 => 16,
u32 || s32 => 32,
u64 || s64 => 64,
};
int get sizeInBytes => switch (this) {
u8 || s8 => 1,
u16 || s16 => 2,
u32 || s32 => 4,
u64 || s64 => 8,
};
int get log2sizeInBytes => switch (this) {
u8 || s8 => 0,
u16 || s16 => 1,
u32 || s32 => 2,
u64 || s64 => 3,
};
}
/// Immediate operand.
class Immediate implements Operand {
final int value;
const Immediate(this.value);
}
/// Address operand.
abstract interface class Address implements Operand {}
/// [base + offset] address operand.
class RegOffsetAddress implements Address {
final Register base;
final int offset;
RegOffsetAddress(this.base, this.offset);
}
/// Destination of a branch.
class Label {
int _offset = -1;
final branchOffsets = <int>[];
Label();
bool get isBound => _offset >= 0;
// Returns relative offset from the branch to the label,
// or 0 if label is not bound yet.
int relativeBranchOffset(int branchOffset) {
if (isBound) {
return _offset - branchOffset;
}
branchOffsets.add(branchOffset);
return 0;
}
void bindTo(int offset) {
assert(offset >= 0);
assert(!isBound);
_offset = offset;
}
}
enum Condition {
unconditional,
equal,
notEqual,
less,
lessOrEqual,
greater,
greaterOrEqual,
unsignedLess,
unsignedLessOrEqual,
unsignedGreater,
unsignedGreaterOrEqual,
negative,
positiveOrZero,
overflow,
noOverflow;
static const Condition zero = equal;
static const Condition notZero = notEqual;
}
/// Base class for architecture-specific assembler.
///
/// Contains declarations of macro-instructions
/// which can be used on all architectures.
abstract base class Assembler {
final VMOffsets vmOffsets;
final ObjectPool objectPool = ObjectPool();
Assembler(this.vmOffsets);
Uint8List get bytes;
/// Create a [base + offset] address for arbitrary offset,
/// generating extra code if necessary.
/// The resulting address can be used in ordinary load/store instructions.
Address address(Register base, int offset, [OperandSize sz]);
/// Create an address for an field of Dart instance.
Address fieldAddress(Register obj, int offset) =>
address(obj, offset - heapObjectTag);
// Push and pop values using Dart stackPointerReg.
void push(Register reg);
void pop(Register reg);
void pushPair(Register low, Register high);
void popPair(Register low, Register high);
// Labels and branches.
void bind(Label label);
void jump(Label label);
void branchIf(Condition condition, Label label);
void loadFromPool(Register reg, Object obj);
void loadConstant(Register reg, ConstantValue value);
/// Load arbitrary integer [value] into register.
void loadImmediate(Register reg, int value);
void callRuntime(RuntimeEntry entry, int argumentCount);
void unimplemented(String message);
}
/// Assembler output buffer holding 32-bit instructions.
mixin Uint32OutputBuffer {
static const int initialSize = 16;
Uint32List _buffer = Uint32List(initialSize);
int _length = 0;
int get length => _length;
@pragma("vm:prefer-inline")
void emit(int instr) {
if (_length < _buffer.length) {
_buffer[_length++] = instr;
} else {
_expandAndEmit(instr);
}
}
@pragma("vm:never-inline")
void _expandAndEmit(int instr) {
final Uint32List old = _buffer;
_buffer = Uint32List(old.length << 1);
_buffer.setRange(0, old.length, old);
_buffer[_length++] = instr;
}
int getAt(int offset) => _buffer[offset];
void setAt(int offset, int instr) {
_buffer[offset] = instr;
}
Uint8List get bytes => _buffer.buffer.asUint8List(0, _length << 2);
}