blob: 74c687f06eb373d008d000620a85d1e567056ed5 [file] [log] [blame]
// Copyright (c) 2026, 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/instructions.dart';
import 'package:cfg/utils/misc.dart';
import 'package:native_compiler/back_end/arm64/assembler.dart';
import 'package:native_compiler/back_end/locations.dart';
import 'package:native_compiler/back_end/stack_frame.dart';
/// Stack frame layout used on arm64.
///
/// Currently stack frame has the following layout (stack grows down):
/// ```
/// [param 1]
/// ...
/// [param N]
/// [saved LR]
/// FP -> [saved FP]
/// [Code]
/// [saved tagged ObjectPool]
/// [shadow space for optional parameters]
/// [spill slot 0]
/// ...
/// [spill slot M]
/// [outgoing arguments area]
/// ```
/// TODO: add catch block entry parameters area.
final class Arm64StackFrame extends StackFrame {
/// Number of fixed frame slots; distance between the last parameter
/// slot and the first spill slot in words.
static const int numberOfFixedSlots = 4;
/// Offset of the last parameter slot, relative to FP
static const int lastParameterOffsetFromFP = 2 * wordSize;
/// Offset of the saved pool pointer relative to FP.
static const int poolPointerOffsetFromFP = -2 * wordSize;
/// Offset of the first shadow parameter, relative to FP
static const int shadowParametersOffsetFromFP = -3 * wordSize;
/// Stack frame alignment.
static const int alignment = 2 * wordSize;
/// Number of stack slots reserved for shadow parameters.
late final int _shadowParametersStackSlots =
((function.hasOptionalPositionalParameters ||
function.hasNamedParameters) &&
function.numberOfParameters > argumentRegisters.length)
? function.numberOfParameters - argumentRegisters.length
: 0;
late final int _firstSpillSlotOffsetFromFP =
shadowParametersOffsetFromFP - _shadowParametersStackSlots * wordSize;
Arm64StackFrame(super.function);
@override
int spillSlotSizeInWords(RegisterClass registerClass) => 1;
@override
int spillSlotAlignmentInWords(RegisterClass registerClass) => 1;
@override
int argumentsStackSlots(CallInstruction instr) {
// TODO: pass arguments on registers
return instr.inputCount;
}
@override
int offsetFromFP(StackLocation location) {
assert(isFinalized);
switch (location) {
case SpillSlot():
return _firstSpillSlotOffsetFromFP - location.index * wordSize;
case ParameterStackLocation():
final paramIndex = location.paramIndex;
final numParams = function.numberOfParameters;
assert(0 <= paramIndex && paramIndex < numParams);
if (function.hasOptionalPositionalParameters ||
function.hasNamedParameters) {
return shadowParameterOffsetFromFP(paramIndex);
} else {
return lastParameterOffsetFromFP +
(numParams - paramIndex - 1) * wordSize;
}
}
}
@override
int shadowParameterOffsetFromFP(int paramIndex) {
assert(
function.hasOptionalPositionalParameters || function.hasNamedParameters,
);
assert(paramIndex >= argumentRegisters.length);
assert(paramIndex < function.numberOfParameters);
return shadowParametersOffsetFromFP -
(paramIndex - argumentRegisters.length) * wordSize;
}
@override
int get frameSizeToAllocate => roundUp(
(_shadowParametersStackSlots + usedSpillSlots + maxArgumentsStackSlots) *
wordSize,
alignment,
);
}