blob: 65845bc323a5d3a3299c1e8d7da82ca62331da76 [file]
// 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]
/// [suspend state (only for async/async*/sync* functions)]
/// [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 {
/// Stack frame alignment.
static const int alignment = 2 * wordSize;
/// 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 suspend state, relative to FP
static const int _suspendStateOffsetFromFP = -3 * wordSize;
/// Number of stack slots used by suspend state.
late final int _suspendStateStackSlots = (function.isSuspendable ? 1 : 0);
/// Offset of the first shadow parameter, relative to FP
late final int _shadowParametersOffsetFromFP =
_suspendStateOffsetFromFP - _suspendStateStackSlots * 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(Instruction instr) {
// TODO: pass arguments on registers
switch (instr) {
case CallInstruction():
return instr.inputCount;
case AllocateClosure():
return 4; // Result + 3 arguments for AllocateClosure runtime call.
case AllocateContext():
return 2; // Result + 1 argument for AllocateContext runtime call.
case AllocateList():
return 3; // Result + 2 arguments for AllocateList runtime call.
case AllocateRecord():
return 2; // Result + 1 argument for AllocateRecord runtime call.
case TypeLiteral():
return 4; // Result + 3 arguments for InstantiateType runtime call.
case TypeCast():
return 3; // Result + 2 arguments for TypeError runtime call.
case TypeTest():
return 6; // Result + 5 arguments for Instanceof runtime call.
case Suspend(:var op) when op == .asyncYield || op == .asyncYieldStar:
return 2; // 2 arguments for _AsyncStarStreamController.add/addStream call.
case Throw(kind: .exception):
return 2; // Result + 1 argument for Throw runtime call.
case Throw(kind: .rethrowException):
return 4; // Result + 3 argument for ReThrow runtime call.
default:
return 0;
}
}
@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 get suspendStateOffsetFromFP {
assert(function.isSuspendable);
return _suspendStateOffsetFromFP;
}
@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(
(_suspendStateStackSlots +
_shadowParametersStackSlots +
usedSpillSlots +
maxArgumentsStackSlots) *
wordSize,
alignment,
);
}