blob: b2d50812b5fce55ea63f266727e39b35f118be23 [file] [log] [blame] [edit]
// 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 'package:cfg/ir/constant_value.dart' show ConstantValue;
import 'package:cfg/ir/instructions.dart' show MoveOp;
/// A generic operand of a machine instruction.
abstract interface class Operand {}
/// Kind of physical register.
enum RegisterClass {
/// General-purpose registers.
cpu,
/// Floating-point / SIMD registers.
fpu,
}
/// Register allocation constraint for one input/output/temporary
/// location used by an IR instruction.
abstract interface class Constraint {
RegisterClass get registerClass;
}
/// Location of an IR instruction input, output or temporary.
abstract interface class Location {
Location get physicalLocation;
}
abstract base class PhysicalRegister implements Constraint, Location, Operand {
const PhysicalRegister();
int get index;
@override
Location get physicalLocation => this;
}
/// General-purpose register.
final class Register extends PhysicalRegister {
final int index;
final String name;
const Register(this.index, this.name);
@override
RegisterClass get registerClass => RegisterClass.cpu;
@override
String toString() => name;
}
const Register invalidReg = Register(-1, 'INVALID');
/// Floating-point register.
final class FPRegister extends PhysicalRegister {
final int index;
final String name;
const FPRegister(this.index, this.name);
@override
RegisterClass get registerClass => RegisterClass.fpu;
@override
String toString() => name;
}
const FPRegister invalidFPReg = FPRegister(-1, 'INVALID');
/// Base class for all stack locations.
sealed class StackLocation implements Location {
const StackLocation();
@override
Location get physicalLocation => this;
}
/// Spill slot.
final class SpillSlot extends StackLocation {
/// Index of the spill slot (0, 1, ...).
final int index;
const SpillSlot(this.index);
@override
String toString() => 'stack[$index]';
}
/// Constraint and location for the parameter passed on the stack.
final class ParameterStackLocation extends StackLocation implements Constraint {
/// Parameter index (0, 1, ..., function.numberOfParameters-1).
final int paramIndex;
@override
final RegisterClass registerClass;
const ParameterStackLocation(this.paramIndex, this.registerClass);
@override
String toString() => 'param[$paramIndex]';
}
/// Location which can be allocated by register allocator.
final class VirtualLocation implements Location {
/// Physical location.
Location? location;
@override
Location get physicalLocation => location!;
@override
String toString() => 'vloc${location != null ? ':$location' : ''}';
}
extension type const OperandId._(int _raw) {
static const int instructionIdMask = 0xffffffff;
static const int operandIdexShift = 32;
OperandId.result(int instructionId) : _raw = instructionId;
OperandId.input(int instructionId, int inputIndex)
: _raw = instructionId | ((inputIndex + 1) << operandIdexShift);
OperandId.temp(int instructionId, int tempIndex)
: _raw = instructionId | ((-tempIndex - 1) << operandIdexShift);
int get instructionId => _raw & instructionIdMask;
int get operandIndex => _raw >> operandIdexShift;
bool get isResult => operandIndex == 0;
bool get isInput => operandIndex > 0;
bool get isTemp => operandIndex < 0;
int get inputIndex {
assert(isInput);
return operandIndex - 1;
}
int get tempIndex {
assert(isTemp);
return -operandIndex - 1;
}
}
/// Locations of instruction inputs, result and
/// temporaries needed to generate code for the instruction.
///
/// TODO: encode locations as int/Uint32List.
class Locations {
Location? result;
List<Location?> inputs;
List<Location?> temps;
Locations(int numInputs, int numTemps)
: inputs = List<Location?>.filled(numInputs, null),
temps = List<Location?>.filled(numTemps, null);
void setOperandLocation(OperandId operandId, Location loc) {
if (operandId.isResult) {
result = loc;
} else if (operandId.isInput) {
inputs[operandId.inputIndex] = loc;
} else {
temps[operandId.tempIndex] = loc;
}
}
}
/// Location->location move, part of ParallelMove instruction.
final class Move extends MoveOp {
Location from;
Location to;
Move(this.from, this.to);
@override
String toString() => '$from -> $to';
}
/// Constant->location move, part of ParallelMove instruction.
final class LoadConstant extends MoveOp {
ConstantValue value;
Location to;
LoadConstant(this.value, this.to);
@override
String toString() => '$value -> $to';
}