diff options
| author | Richard-Qin-X <richard.qin.001@gmail.com> | 2026-08-12 22:37:49 +0800 |
|---|---|---|
| committer | Richard-Qin-X <richard.qin.001@gmail.com> | 2026-08-12 22:37:49 +0800 |
| commit | 3341938bb644e6fc39afa6a8fcb1306604999e42 (patch) | |
| tree | a7586c0a8a9d3e7e9dbc8709daf4dd76ca51be5f /backend/lib/Target/RISCV/RISCVCallLowering.cpp | |
| parent | 0bb35fa2306a66f1f0baf118619b5c1640ed9c01 (diff) | |
| download | rcc-project-3341938bb644e6fc39afa6a8fcb1306604999e42.tar.gz rcc-project-3341938bb644e6fc39afa6a8fcb1306604999e42.tar.xz | |
[RISCV] Split the GlobalISel target components
Diffstat (limited to 'backend/lib/Target/RISCV/RISCVCallLowering.cpp')
| -rw-r--r-- | backend/lib/Target/RISCV/RISCVCallLowering.cpp | 1512 |
1 files changed, 0 insertions, 1512 deletions
diff --git a/backend/lib/Target/RISCV/RISCVCallLowering.cpp b/backend/lib/Target/RISCV/RISCVCallLowering.cpp deleted file mode 100644 index 5da3fc8..0000000 --- a/backend/lib/Target/RISCV/RISCVCallLowering.cpp +++ /dev/null @@ -1,1512 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "rcc/Target/RISCV/RISCVCallLowering.h" - -#include "rcc/CodeGen/GenericOpcodes.h" -#include "rcc/CodeGen/MachineBasicBlock.h" -#include "rcc/CodeGen/MachineFrameInfo.h" -#include "rcc/CodeGen/MachineFunction.h" -#include "rcc/CodeGen/MachineIRBuilder.h" -#include "rcc/CodeGen/MachineInstr.h" -#include "rcc/CodeGen/MachineMemOperand.h" -#include "rcc/CodeGen/MachineRegisterInfo.h" -#include "rcc/CodeGen/TargetDescription.h" -#include "rcc/Target/RISCV/RISCVTarget.h" - -#include <algorithm> -#include <array> -#include <cstddef> -#include <cstdint> -#include <limits> -#include <optional> -#include <span> -#include <string> -#include <unordered_map> -#include <unordered_set> -#include <utility> -#include <vector> - -namespace rcc::riscv { -namespace { - -constexpr std::string_view PlanTarget = "riscv64-unknown-linux-gnu"; -constexpr std::uint64_t XLenBytes = 8; -constexpr std::uint64_t ABIStackAlignmentBytes = 16; -constexpr std::uint32_t AllowedDirectCallFlags = - static_cast<std::uint32_t>(CallTargetFlag::DirectCall); - -[[nodiscard]] Opcode targetOpcode(InstructionOpcode Value) noexcept { - return Opcode::fromValue(static_cast<std::uint32_t>(Value)); -} - -[[nodiscard]] std::uint64_t stackObjectFlag(CallStackObjectFlag Flag) noexcept { - return static_cast<std::uint64_t>(Flag); -} - -[[nodiscard]] Expected<Alignment> alignment(std::uint64_t Bytes) { - return Alignment::create(Bytes); -} - -[[nodiscard]] Expected<std::uint64_t> alignTo(std::uint64_t Value, std::uint64_t AlignmentBytes) { - const std::uint64_t Remainder = Value % AlignmentBytes; - if (Remainder == 0) - return Value; - const std::uint64_t Increment = AlignmentBytes - Remainder; - if (Value > std::numeric_limits<std::uint64_t>::max() - Increment) { - return makeUnexpected(ErrorCode::Unsupported, "RISC-V ABI stack-area size overflows uint64"); - } - return Value + Increment; -} - -[[nodiscard]] std::uint64_t valueSizeBytes(LLT Type) noexcept { - return (static_cast<std::uint64_t>(Type.scalarBitWidth()) + 7U) / 8U; -} - -[[nodiscard]] bool isSupportedFloat(LLT Type) noexcept { - if (!Type.isFloat()) - return false; - const auto Semantics = Type.floatSemantics(); - return Semantics && - (*Semantics == FloatSemantics::IEEESingle || *Semantics == FloatSemantics::IEEEDouble); -} - -[[nodiscard]] Expected<PhysicalRegister> floatingRegisterView(PhysicalRegister DoubleView, - LLT Type) { - if (!isSupportedFloat(Type)) { - return makeUnexpected(ErrorCode::InvalidArgument, - "RISC-V floating-register view requires f32 or f64"); - } - if (Type.scalarBitWidth() == 64) - return DoubleView; - - constexpr std::uint32_t FirstDouble = static_cast<std::uint32_t>(PhysicalRegisterID::F0_D); - constexpr std::uint32_t LastDouble = static_cast<std::uint32_t>(PhysicalRegisterID::F31_D); - constexpr std::uint32_t FirstSingle = static_cast<std::uint32_t>(PhysicalRegisterID::F0_F); - const std::uint32_t Number = DoubleView.number(); - if (Number < FirstDouble || Number > LastDouble) { - return makeUnexpected(ErrorCode::InternalError, - "RISC-V ABI floating register is not an FPR64 view"); - } - return PhysicalRegister::create(FirstSingle + Number - FirstDouble); -} - -[[nodiscard]] bool requiresIntegerTransport(const ABIValue &Value) noexcept { - return !isSupportedFloat(Value.Type) || Value.Flags.has(ABIValueFlag::ByVal) || - Value.Flags.has(ABIValueFlag::StructReturn) || Value.Flags.has(ABIValueFlag::Indirect); -} - -[[nodiscard]] Expected<void> verifyABIValue(const ABIValue &Value, bool RequireRegister) { - if (RequireRegister && Value.Value.isNoRegister()) { - return makeUnexpected(ErrorCode::InvalidArgument, - "RISC-V ABI value requires a source or destination register"); - } - if (!Value.Type.isValid() || !Value.Type.isScalarValue() || Value.Type.scalarBitWidth() > 64) { - return makeUnexpected( - ErrorCode::Unsupported, - "RISC-V ABI accepts explicitly classified integer, pointer, f32, and f64 scalar fragments"); - } - if (Value.Type.isFloat() && !isSupportedFloat(Value.Type)) { - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V LP64 ABIs only support f32 and f64 floating fragments"); - } - if (Value.Type.isPointer() && Value.Type.scalarBitWidth() != 64) { - return makeUnexpected(ErrorCode::Unsupported, "RISC-V LP64 ABIs require 64-bit pointers"); - } - if ((Value.Type.isInteger() || Value.Type.isAnyScalar()) && Value.Type.scalarBitWidth() < 64) { - if (Value.Extension == ABIExtensionKind::None) { - return makeUnexpected(ErrorCode::InvalidArgument, - "narrow RISC-V ABI integers require an explicit extension kind"); - } - } else if (Value.Extension != ABIExtensionKind::None) { - return makeUnexpected(ErrorCode::InvalidArgument, - "only narrow integer ABI fragments may carry an extension kind"); - } - if (Value.Extension >= ABIExtensionKind::Count || Value.FragmentCount == 0 || - Value.FragmentIndex >= Value.FragmentCount || Value.FragmentCount > 2) { - return makeUnexpected(ErrorCode::InvalidArgument, - "RISC-V ABI fragment coordinates or extension are invalid"); - } - - const bool IsByVal = Value.Flags.has(ABIValueFlag::ByVal); - const bool IsSRet = Value.Flags.has(ABIValueFlag::StructReturn); - const bool IsIndirect = Value.Flags.has(ABIValueFlag::Indirect); - const unsigned SpecialFlagCount = static_cast<unsigned>(IsByVal) + static_cast<unsigned>(IsSRet) + - static_cast<unsigned>(IsIndirect); - if (SpecialFlagCount > 1) { - return makeUnexpected(ErrorCode::InvalidArgument, - "byval, sret, and indirect ABI flags are mutually exclusive"); - } - if ((IsByVal || IsSRet || IsIndirect) && - (!Value.Type.isPointer() || Value.FragmentCount != 1 || Value.FragmentIndex != 0 || - Value.FragmentOffsetBytes != 0)) { - return makeUnexpected(ErrorCode::InvalidArgument, - "byval, sret, and indirect values require one explicit pointer fragment"); - } - if (IsByVal) { - if (Value.ByValSizeBytes == 0) { - return makeUnexpected(ErrorCode::InvalidArgument, - "byval arguments require a nonzero copy size"); - } - } else if (Value.ByValSizeBytes != 0) { - return makeUnexpected(ErrorCode::InvalidArgument, - "non-byval values cannot carry a byval copy size"); - } - return {}; -} - -[[nodiscard]] Expected<std::size_t> verifyValueSequence(std::span<const ABIValue> Values, - bool RequireRegisters) { - std::size_t ExpectedOriginal = 0; - std::size_t Index = 0; - while (Index < Values.size()) { - const ABIValue &First = Values[Index]; - auto ValueVerification = verifyABIValue(First, RequireRegisters); - if (!ValueVerification) - return std::unexpected(ValueVerification.error()); - if (First.OriginalValue != ExpectedOriginal || First.FragmentIndex != 0 || - Index + First.FragmentCount > Values.size()) { - return makeUnexpected( - ErrorCode::InvalidArgument, - "RISC-V ABI values must form contiguous, zero-based original-value groups"); - } - - std::uint64_t PreviousEnd = 0; - for (std::size_t Fragment = 0; Fragment < First.FragmentCount; ++Fragment) { - const ABIValue &Value = Values[Index + Fragment]; - auto FragmentVerification = verifyABIValue(Value, RequireRegisters); - if (!FragmentVerification) - return std::unexpected(FragmentVerification.error()); - if (Value.OriginalValue != First.OriginalValue || Value.FragmentIndex != Fragment || - Value.FragmentCount != First.FragmentCount || - Value.OriginalAlignment != First.OriginalAlignment || Value.Flags != First.Flags || - Value.ByValSizeBytes != First.ByValSizeBytes || - Value.ByValAlignment != First.ByValAlignment || Value.FragmentOffsetBytes < PreviousEnd) { - return makeUnexpected(ErrorCode::InvalidArgument, - "RISC-V ABI fragments of one value have inconsistent metadata"); - } - PreviousEnd = - static_cast<std::uint64_t>(Value.FragmentOffsetBytes) + valueSizeBytes(Value.Type); - } - Index += First.FragmentCount; - ++ExpectedOriginal; - } - return ExpectedOriginal; -} - -[[nodiscard]] CCAssignmentFlags assignmentFlags(const ABIValue &Value, bool IsVariadic, - bool IsBitcast) { - CCAssignmentFlags Result; - if (IsVariadic) - Result.set(CCAssignmentFlag::Variadic); - if (Value.Flags.has(ABIValueFlag::ByVal)) - Result.set(CCAssignmentFlag::ByVal); - if (Value.Flags.has(ABIValueFlag::StructReturn)) - Result.set(CCAssignmentFlag::StructReturn); - if (Value.Flags.has(ABIValueFlag::Indirect)) - Result.set(CCAssignmentFlag::Indirect); - if (Value.Flags.has(ABIValueFlag::InRegister)) - Result.set(CCAssignmentFlag::InRegister); - if (IsBitcast) - Result.set(CCAssignmentFlag::Bitcast); - return Result; -} - -[[nodiscard]] CCLocationKind locationKind(const ABIValue &Value, bool IsRegister) noexcept { - if (Value.Flags.has(ABIValueFlag::Indirect)) - return CCLocationKind::Indirect; - return IsRegister ? CCLocationKind::Register : CCLocationKind::Stack; -} - -struct ArgumentAssignmentResult final { - std::vector<CCAssignment> Assignments; - StackArgumentArea StackArea; - std::size_t FirstUnallocatedGPR = 0; -}; - -struct AssignmentState final { - std::size_t GPR = 0; - std::size_t FPR = 0; - std::uint64_t StackOffset = 0; - bool VariadicStackOnly = false; -}; - -[[nodiscard]] Expected<CCAssignment> registerAssignment(const ABIValue &Value, - std::size_t ValueIndex, - PhysicalRegister Location, LLT LocationType, - bool IsVariadic, bool IsBitcast) { - auto Align = - alignment(std::min<std::uint64_t>(Value.OriginalAlignment.bytes(), ABIStackAlignmentBytes)); - if (!Align) - return std::unexpected(Align.error()); - return CCAssignment{ - .ValueIndex = ValueIndex, - .OriginalValue = Value.Value, - .OriginalValueIndex = Value.OriginalValue, - .FragmentIndex = Value.FragmentIndex, - .FragmentCount = Value.FragmentCount, - .ValueType = Value.Type, - .LocationType = LocationType, - .LocationKind = locationKind(Value, true), - .LocationRegister = Location, - .StackOffsetBytes = std::nullopt, - .LocationSizeBytes = valueSizeBytes(LocationType), - .StackSlotSizeBytes = XLenBytes, - .LocationAlignment = *Align, - .Extension = Value.Extension, - .CustomFlags = assignmentFlags(Value, IsVariadic, IsBitcast), - }; -} - -[[nodiscard]] Expected<CCAssignment> stackAssignment(const ABIValue &Value, std::size_t ValueIndex, - std::uint64_t StackOffset, LLT LocationType, - bool IsVariadic, - std::uint64_t StackAlignment) { - auto Align = alignment(StackAlignment); - if (!Align) - return std::unexpected(Align.error()); - return CCAssignment{ - .ValueIndex = ValueIndex, - .OriginalValue = Value.Value, - .OriginalValueIndex = Value.OriginalValue, - .FragmentIndex = Value.FragmentIndex, - .FragmentCount = Value.FragmentCount, - .ValueType = Value.Type, - .LocationType = LocationType, - .LocationKind = locationKind(Value, false), - .LocationRegister = std::nullopt, - .StackOffsetBytes = StackOffset, - .LocationSizeBytes = valueSizeBytes(LocationType), - .StackSlotSizeBytes = XLenBytes, - .LocationAlignment = *Align, - .Extension = Value.Extension, - .CustomFlags = assignmentFlags(Value, IsVariadic, false), - }; -} - -[[nodiscard]] Expected<ArgumentAssignmentResult> -assignArguments(std::span<const ABIValue> Values, std::size_t FixedArgumentCount, bool IsVariadic, - const CallingConventionInfo &Convention) { - auto I64 = LLT::makeInteger(64); - auto StackAlign = alignment(Convention.StackArgumentAlignmentBytes); - if (!I64 || !StackAlign) - return makeUnexpected(ErrorCode::InternalError, "RISC-V ABI primitive type facts are invalid"); - - ArgumentAssignmentResult Result; - AssignmentState State; - State.StackOffset = Convention.ShadowSpaceBytes; - Result.Assignments.reserve(Values.size()); - - std::size_t Index = 0; - while (Index < Values.size()) { - const ABIValue &First = Values[Index]; - const std::size_t GroupSize = First.FragmentCount; - const bool VariadicArgument = IsVariadic && First.OriginalValue >= FixedArgumentCount; - bool HasPreferredFloat = false; - std::size_t RequiredGPRs = 0; - std::size_t RequiredFPRs = 0; - for (std::size_t Fragment = 0; Fragment < GroupSize; ++Fragment) { - const ABIValue &Value = Values[Index + Fragment]; - if (!VariadicArgument && !requiresIntegerTransport(Value)) { - HasPreferredFloat = true; - ++RequiredFPRs; - } else { - ++RequiredGPRs; - } - } - - const bool CanUsePreferredAggregate = - HasPreferredFloat && - State.GPR + RequiredGPRs <= Convention.IntegerArgumentRegisters.size() && - State.FPR + RequiredFPRs <= Convention.FloatingArgumentRegisters.size(); - if (CanUsePreferredAggregate) { - for (std::size_t Fragment = 0; Fragment < GroupSize; ++Fragment) { - const ABIValue &Value = Values[Index + Fragment]; - Expected<CCAssignment> Assignment = makeUnexpected( - ErrorCode::InternalError, "RISC-V ABI argument assignment was not initialized"); - if (requiresIntegerTransport(Value)) { - Assignment = registerAssignment(Value, Index + Fragment, - Convention.IntegerArgumentRegisters[State.GPR++], *I64, - false, false); - } else { - auto View = - floatingRegisterView(Convention.FloatingArgumentRegisters[State.FPR++], Value.Type); - if (!View) - return std::unexpected(View.error()); - Assignment = registerAssignment(Value, Index + Fragment, *View, Value.Type, false, false); - } - if (!Assignment) - return std::unexpected(Assignment.error()); - Result.Assignments.push_back(*Assignment); - } - Index += GroupSize; - continue; - } - - const bool RequiresAlignedPair = - VariadicArgument && GroupSize == 2 && First.OriginalAlignment.bytes() >= 16; - if (RequiresAlignedPair && State.GPR % 2 != 0 && - State.GPR < Convention.IntegerArgumentRegisters.size()) { - ++State.GPR; - } - bool ForceEntireGroupToStack = State.VariadicStackOnly; - if (RequiresAlignedPair && State.GPR + GroupSize > Convention.IntegerArgumentRegisters.size()) { - ForceEntireGroupToStack = true; - State.GPR = Convention.IntegerArgumentRegisters.size(); - } - - bool FirstStackFragment = true; - for (std::size_t Fragment = 0; Fragment < GroupSize; ++Fragment) { - const ABIValue &Value = Values[Index + Fragment]; - const bool HasGPR = - !ForceEntireGroupToStack && State.GPR < Convention.IntegerArgumentRegisters.size(); - if (HasGPR) { - const bool Bitcast = isSupportedFloat(Value.Type); - auto Assignment = registerAssignment(Value, Index + Fragment, - Convention.IntegerArgumentRegisters[State.GPR++], *I64, - VariadicArgument, Bitcast); - if (!Assignment) - return std::unexpected(Assignment.error()); - Result.Assignments.push_back(*Assignment); - continue; - } - - if (Value.Flags.has(ABIValueFlag::InRegister)) { - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V inreg argument cannot be assigned to the stack"); - } - std::uint64_t RequiredAlignment = XLenBytes; - if (FirstStackFragment && ForceEntireGroupToStack) { - RequiredAlignment = std::min<std::uint64_t>( - std::max<std::uint64_t>(XLenBytes, First.OriginalAlignment.bytes()), - ABIStackAlignmentBytes); - } - auto AlignedOffset = alignTo(State.StackOffset, RequiredAlignment); - if (!AlignedOffset) - return std::unexpected(AlignedOffset.error()); - State.StackOffset = *AlignedOffset; - const LLT LocationType = isSupportedFloat(Value.Type) ? Value.Type : *I64; - auto Assignment = stackAssignment(Value, Index + Fragment, State.StackOffset, LocationType, - VariadicArgument, RequiredAlignment); - if (!Assignment) - return std::unexpected(Assignment.error()); - Result.Assignments.push_back(*Assignment); - if (State.StackOffset > std::numeric_limits<std::uint64_t>::max() - XLenBytes) { - return makeUnexpected(ErrorCode::Unsupported, "RISC-V argument area overflows uint64"); - } - State.StackOffset += XLenBytes; - FirstStackFragment = false; - if (VariadicArgument) - State.VariadicStackOnly = true; - } - Index += GroupSize; - } - - auto FinalSize = alignTo(State.StackOffset, Convention.StackArgumentAlignmentBytes); - if (!FinalSize) - return std::unexpected(FinalSize.error()); - Result.StackArea = StackArgumentArea{*FinalSize, Convention.ShadowSpaceBytes, *StackAlign}; - Result.FirstUnallocatedGPR = State.GPR; - return Result; -} - -[[nodiscard]] Expected<std::vector<CCAssignment>> -assignReturns(std::span<const ABIValue> Values, bool RequireRegisters, - const CallingConventionInfo &Convention) { - auto Sequence = verifyValueSequence(Values, RequireRegisters); - if (!Sequence) - return std::unexpected(Sequence.error()); - if (Values.size() > 2) { - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V direct returns are limited to two explicit fragments"); - } - auto I64 = LLT::makeInteger(64); - if (!I64) - return std::unexpected(I64.error()); - - std::vector<CCAssignment> Result; - Result.reserve(Values.size()); - std::size_t GPR = 0; - std::size_t FPR = 0; - for (std::size_t Index = 0; Index < Values.size(); ++Index) { - const ABIValue &Value = Values[Index]; - if (Value.Flags.rawValue() != 0) { - return makeUnexpected(ErrorCode::InvalidArgument, - "return fragments cannot carry argument-only ABI flags"); - } - const bool UseFloatingRegister = - isSupportedFloat(Value.Type) && FPR < Convention.FloatingReturnRegisters.size(); - Expected<CCAssignment> Assignment = makeUnexpected( - ErrorCode::Unsupported, "RISC-V calling convention has no return register available"); - if (UseFloatingRegister) { - auto View = floatingRegisterView(Convention.FloatingReturnRegisters[FPR++], Value.Type); - if (!View) - return std::unexpected(View.error()); - Assignment = registerAssignment(Value, Index, *View, Value.Type, false, false); - } else if (GPR < Convention.IntegerReturnRegisters.size()) { - Assignment = registerAssignment(Value, Index, Convention.IntegerReturnRegisters[GPR++], *I64, - false, isSupportedFloat(Value.Type)); - } - if (!Assignment) - return std::unexpected(Assignment.error()); - Result.push_back(*Assignment); - } - return Result; -} - -[[nodiscard]] bool hasStructReturn(std::span<const ABIValue> Arguments) noexcept { - return std::ranges::any_of( - Arguments, [](const ABIValue &Value) { return Value.Flags.has(ABIValueFlag::StructReturn); }); -} - -[[nodiscard]] bool equivalentReturnLocations(std::span<const CCAssignment> Left, - std::span<const CCAssignment> Right) noexcept { - if (Left.size() != Right.size()) - return false; - for (std::size_t Index = 0; Index < Left.size(); ++Index) { - if (Left[Index].ValueType != Right[Index].ValueType || - Left[Index].LocationType != Right[Index].LocationType || - Left[Index].LocationKind != Right[Index].LocationKind || - Left[Index].LocationRegister != Right[Index].LocationRegister || - Left[Index].Extension != Right[Index].Extension || - Left[Index].CustomFlags != Right[Index].CustomFlags) { - return false; - } - } - return true; -} - -[[nodiscard]] Expected<std::string> -tailCallRejection(const CallLoweringRequest &Request, const ArgumentAssignmentResult &Arguments, - std::span<const CCAssignment> Results, - std::span<const ByValCopyAssignment> ByValCopies, - const CallingConventionInfo &Convention) { - if (!Convention.SupportsTailCalls) - return std::string("the calling convention does not support tail calls"); - if (!Request.TailContext) - return std::string("tail-call context is missing"); - const TailCallContext &Context = *Request.TailContext; - if (Context.CallerConvention != Request.Convention) - return std::string("caller and callee calling conventions differ"); - if (Context.CallerIsVariadic != Request.IsVariadic || - (Request.IsVariadic && Context.CallerFixedArgumentCount != Request.FixedArgumentCount)) { - return std::string("caller and callee variadic signatures differ"); - } - if (Context.CallerHasDynamicStackAllocation) - return std::string("the caller has a dynamic stack allocation"); - if (!ByValCopies.empty()) - return std::string("tail calls cannot create byval copies"); - if (Context.CallerUsesStructReturn != hasStructReturn(Request.Arguments)) - return std::string("caller and callee sret conventions differ"); - if (Arguments.StackArea.SizeBytes > Context.ReusableIncomingArgumentBytes) - return std::string("outgoing stack arguments exceed the reusable incoming area"); - if (std::ranges::any_of(Request.Results, - [](const ABIValue &Value) { return !Value.Value.isNoRegister(); })) { - return std::string("tail-call result signatures must not request local result copies"); - } - auto CallerReturns = assignReturns(Context.CallerReturns, false, Convention); - if (!CallerReturns) - return std::unexpected(CallerReturns.error()); - if (!equivalentReturnLocations(Results, *CallerReturns)) - return std::string("caller and callee return locations differ"); - return std::string{}; -} - -[[nodiscard]] Expected<const CallingConventionInfo *> -riscvConvention(CallingConventionID Convention) { - CallingConvention TargetConvention = CallingConvention::None; - if (Convention == callingConventionID(CallingConvention::LP64D)) - TargetConvention = CallingConvention::LP64D; - else if (Convention == callingConventionID(CallingConvention::LP64)) - TargetConvention = CallingConvention::LP64; - else - return makeUnexpected(ErrorCode::Unsupported, "RISC-V backend only supports LP64 and LP64D"); - const CallingConventionInfo *Info = callingConvention(TargetConvention); - if (Info == nullptr) - return makeUnexpected(ErrorCode::InternalError, - "RISC-V calling-convention target facts are unavailable"); - return Info; -} - -class RISCVCallLowering final : public TargetCallLowering { -public: - [[nodiscard]] Expected<CallLoweringPlan> - planFormalArguments(const FormalArgumentRequest &Request) const override; - [[nodiscard]] Expected<CallLoweringPlan> - planCall(const CallLoweringRequest &Request) const override; - [[nodiscard]] Expected<CallLoweringPlan> - planReturn(const ReturnLoweringRequest &Request) const override; - [[nodiscard]] Expected<void> emitPlan(MachineIRBuilder &Builder, - const CallLoweringPlan &Plan) const override; -}; - -Expected<CallLoweringPlan> -RISCVCallLowering::planFormalArguments(const FormalArgumentRequest &Request) const { - auto Convention = riscvConvention(Request.Convention); - if (!Convention) - return std::unexpected(Convention.error()); - auto OriginalCount = verifyValueSequence(Request.Arguments, true); - if (!OriginalCount) - return std::unexpected(OriginalCount.error()); - if (Request.FixedArgumentCount != *OriginalCount) { - return makeUnexpected(ErrorCode::InvalidArgument, - "formal-argument request must identify every declared argument as fixed"); - } - if (!Request.IsVariadic && Request.FixedArgumentCount != *OriginalCount) { - return makeUnexpected(ErrorCode::InvalidArgument, - "non-variadic formal request has an invalid fixed-argument boundary"); - } - if (hasStructReturn(Request.Arguments) && - (Request.Arguments.empty() || Request.Arguments.front().OriginalValue != 0 || - !Request.Arguments.front().Flags.has(ABIValueFlag::StructReturn))) { - return makeUnexpected(ErrorCode::InvalidArgument, - "RISC-V sret must be the first formal argument"); - } - - auto Assignments = assignArguments(Request.Arguments, Request.FixedArgumentCount, - Request.IsVariadic, **Convention); - if (!Assignments) - return std::unexpected(Assignments.error()); - - VarArgLoweringState VarArgs; - if (Request.IsVariadic) { - VarArgs.IsVariadic = true; - VarArgs.FixedArgumentCount = Request.FixedArgumentCount; - VarArgs.FirstUnallocatedIntegerRegister = Assignments->FirstUnallocatedGPR; - VarArgs.OverflowAreaOffsetBytes = Assignments->StackArea.SizeBytes; - const std::size_t First = Assignments->FirstUnallocatedGPR; - const std::size_t RegisterCount = (*Convention)->IntegerArgumentRegisters.size(); - VarArgs.RegisterSaveAreaSizeBytes = (RegisterCount - First) * XLenBytes; - VarArgs.RegisterSaveAreaOffsetBytes = - VarArgs.RegisterSaveAreaSizeBytes == 0 - ? static_cast<std::int64_t>(VarArgs.OverflowAreaOffsetBytes) - : -static_cast<std::int64_t>(VarArgs.RegisterSaveAreaSizeBytes); - if (VarArgs.RegisterSaveAreaSizeBytes != 0 && First % 2 != 0) - VarArgs.RegisterSavePaddingBytes = XLenBytes; - for (std::size_t Index = First; Index < RegisterCount; ++Index) { - VarArgs.RegistersToSave.push_back( - {(*Convention)->IntegerArgumentRegisters[Index], (Index - First) * XLenBytes}); - } - } - - CallLoweringPlanDesc Plan; - Plan.Kind = CallLoweringPlanKind::FormalArguments; - Plan.Target = PlanTarget; - Plan.Convention = Request.Convention; - Plan.ArgumentAssignments = std::move(Assignments->Assignments); - Plan.StackArguments = Assignments->StackArea; - Plan.VarArgs = std::move(VarArgs); - return CallLoweringPlan::create(std::move(Plan)); -} - -Expected<CallLoweringPlan> RISCVCallLowering::planCall(const CallLoweringRequest &Request) const { - auto Convention = riscvConvention(Request.Convention); - if (!Convention) - return std::unexpected(Convention.error()); - auto OriginalCount = verifyValueSequence(Request.Arguments, true); - if (!OriginalCount) - return std::unexpected(OriginalCount.error()); - if ((!Request.IsVariadic && Request.FixedArgumentCount != *OriginalCount) || - (Request.IsVariadic && Request.FixedArgumentCount > *OriginalCount)) { - return makeUnexpected(ErrorCode::InvalidArgument, - "call request has an invalid fixed/variadic argument boundary"); - } - if (!Request.Callee.isValid()) - return makeUnexpected(ErrorCode::InvalidArgument, "call request requires a valid callee"); - if (hasStructReturn(Request.Arguments) && - (Request.Arguments.empty() || - !Request.Arguments.front().Flags.has(ABIValueFlag::StructReturn))) { - return makeUnexpected(ErrorCode::InvalidArgument, - "RISC-V sret must be the first call argument"); - } - if (hasStructReturn(Request.Arguments) && !Request.Results.empty()) { - return makeUnexpected(ErrorCode::InvalidArgument, - "RISC-V sret calls cannot also request direct return values"); - } - - MachineCallTarget EffectiveCallee = Request.Callee; - switch (Request.Callee.kind()) { - case MachineCallTargetKind::GlobalSymbol: { - if ((Request.Callee.targetFlags() & ~AllowedDirectCallFlags) != 0) { - return makeUnexpected(ErrorCode::InvalidArgument, - "direct RISC-V call uses unknown target flags"); - } - auto Callee = MachineCallTarget::globalSymbol( - std::string(Request.Callee.symbolName()), - Request.Callee.targetFlags() | static_cast<std::uint32_t>(CallTargetFlag::DirectCall)); - if (!Callee) - return std::unexpected(Callee.error()); - EffectiveCallee = std::move(*Callee); - break; - } - case MachineCallTargetKind::ExternalSymbol: { - if ((Request.Callee.targetFlags() & ~AllowedDirectCallFlags) != 0) { - return makeUnexpected(ErrorCode::InvalidArgument, - "direct RISC-V call uses unknown target flags"); - } - auto Callee = MachineCallTarget::externalSymbol( - std::string(Request.Callee.symbolName()), - Request.Callee.targetFlags() | static_cast<std::uint32_t>(CallTargetFlag::DirectCall)); - if (!Callee) - return std::unexpected(Callee.error()); - EffectiveCallee = std::move(*Callee); - break; - } - case MachineCallTargetKind::IndirectRegister: - if (Request.Callee.targetFlags() != 0) { - return makeUnexpected(ErrorCode::InvalidArgument, - "indirect RISC-V calls cannot carry direct-call target flags"); - } - break; - case MachineCallTargetKind::Invalid: - return makeUnexpected(ErrorCode::InvalidArgument, "call target is invalid"); - } - - const bool RequireResultRegisters = Request.TailKind == TailCallKind::NotTail; - auto ResultSequence = verifyValueSequence(Request.Results, RequireResultRegisters); - if (!ResultSequence) - return std::unexpected(ResultSequence.error()); - auto Arguments = assignArguments(Request.Arguments, Request.FixedArgumentCount, - Request.IsVariadic, **Convention); - if (!Arguments) - return std::unexpected(Arguments.error()); - auto Results = assignReturns(Request.Results, RequireResultRegisters, **Convention); - if (!Results) - return std::unexpected(Results.error()); - - auto Align16 = alignment(ABIStackAlignmentBytes); - if (!Align16) - return std::unexpected(Align16.error()); - std::uint64_t CallFrameCursor = Arguments->StackArea.SizeBytes; - std::vector<ByValCopyAssignment> ByValCopies; - for (std::size_t Index = 0; Index < Arguments->Assignments.size(); ++Index) { - const CCAssignment &Assignment = Arguments->Assignments[Index]; - if (!Assignment.CustomFlags.has(CCAssignmentFlag::ByVal)) - continue; - const ABIValue &Value = Request.Arguments[Assignment.ValueIndex]; - const std::uint64_t CopyAlignment = - std::min<std::uint64_t>(Value.ByValAlignment.bytes(), ABIStackAlignmentBytes); - auto CopyOffset = alignTo(CallFrameCursor, CopyAlignment); - if (!CopyOffset) - return std::unexpected(CopyOffset.error()); - if (*CopyOffset > std::numeric_limits<std::uint64_t>::max() - Value.ByValSizeBytes) { - return makeUnexpected(ErrorCode::Unsupported, "RISC-V byval area overflows uint64"); - } - auto CopyAlign = alignment(CopyAlignment); - if (!CopyAlign) - return std::unexpected(CopyAlign.error()); - ByValCopies.push_back( - {Index, Value.Value, Value.Type, *CopyOffset, Value.ByValSizeBytes, *CopyAlign}); - CallFrameCursor = *CopyOffset + Value.ByValSizeBytes; - } - auto CallFrameSize = alignTo(CallFrameCursor, ABIStackAlignmentBytes); - if (!CallFrameSize) - return std::unexpected(CallFrameSize.error()); - - TailCallKind Decision = TailCallKind::NotTail; - std::string RejectionReason; - if (Request.TailKind != TailCallKind::NotTail) { - auto Rejection = tailCallRejection(Request, *Arguments, *Results, ByValCopies, **Convention); - if (!Rejection) - return std::unexpected(Rejection.error()); - RejectionReason = std::move(*Rejection); - if (RejectionReason.empty()) { - Decision = Request.TailKind; - } else if (Request.TailKind == TailCallKind::MustTail) { - return makeUnexpected(ErrorCode::Unsupported, - "musttail call is not ABI-compatible: " + RejectionReason); - } else { - auto NormalResults = verifyValueSequence(Request.Results, true); - if (!NormalResults) - return std::unexpected(NormalResults.error()); - } - } - - CallLoweringPlanDesc Plan; - Plan.Kind = CallLoweringPlanKind::Call; - Plan.Target = PlanTarget; - Plan.Convention = Request.Convention; - Plan.ArgumentAssignments = std::move(Arguments->Assignments); - Plan.ReturnAssignments = std::move(*Results); - Plan.StackArguments = Arguments->StackArea; - Plan.CallFrame = CallFrameRequirements{ - Decision == TailCallKind::NotTail ? *CallFrameSize : Arguments->StackArea.SizeBytes, *Align16, - Decision == TailCallKind::NotTail, Decision == TailCallKind::NotTail, - Decision != TailCallKind::NotTail}; - if (Request.IsVariadic) { - Plan.VarArgs.IsVariadic = true; - Plan.VarArgs.FixedArgumentCount = Request.FixedArgumentCount; - } - Plan.ByValCopies = std::move(ByValCopies); - Plan.PreservedMask = createCallPreservedMask((*Convention)->ID); - if (!Plan.PreservedMask) - return makeUnexpected(ErrorCode::InternalError, "RISC-V call-preserved mask is unavailable"); - Plan.Callee = std::move(EffectiveCallee); - Plan.TailDecision = Decision; - Plan.TailRejectionReason = std::move(RejectionReason); - return CallLoweringPlan::create(std::move(Plan)); -} - -Expected<CallLoweringPlan> -RISCVCallLowering::planReturn(const ReturnLoweringRequest &Request) const { - auto Convention = riscvConvention(Request.Convention); - if (!Convention) - return std::unexpected(Convention.error()); - auto Values = assignReturns(Request.Values, true, **Convention); - if (!Values) - return std::unexpected(Values.error()); - - CallLoweringPlanDesc Plan; - Plan.Kind = CallLoweringPlanKind::Return; - Plan.Target = PlanTarget; - Plan.Convention = Request.Convention; - Plan.ReturnAssignments = std::move(*Values); - return CallLoweringPlan::create(std::move(Plan)); -} - -[[nodiscard]] CallValueTransform outgoingTransform(const CCAssignment &Assignment) noexcept { - if (Assignment.CustomFlags.has(CCAssignmentFlag::Bitcast)) - return CallValueTransform::Bitcast; - switch (Assignment.Extension) { - case ABIExtensionKind::None: - return CallValueTransform::Full; - case ABIExtensionKind::AnyExtend: - return CallValueTransform::AnyExtend; - case ABIExtensionKind::SignExtend: - return CallValueTransform::SignExtend; - case ABIExtensionKind::ZeroExtend: - return CallValueTransform::ZeroExtend; - case ABIExtensionKind::Count: - break; - } - return CallValueTransform::Full; -} - -[[nodiscard]] CallValueTransform incomingTransform(const CCAssignment &Assignment) noexcept { - if (Assignment.CustomFlags.has(CCAssignmentFlag::Bitcast)) - return CallValueTransform::Bitcast; - if (Assignment.ValueType.scalarBitWidth() < Assignment.LocationType.scalarBitWidth()) - return CallValueTransform::Truncate; - return CallValueTransform::Full; -} - -[[nodiscard]] Expected<void> addUnsignedImmediate(MachineInstrBuilder &Builder, - std::uint64_t Value) { - auto Immediate = IntegerImmediate::fromUnsigned(64, Value); - if (!Immediate) - return std::unexpected(Immediate.error()); - return Builder.addIntegerImmediate(std::move(*Immediate)); -} - -[[nodiscard]] Expected<const MachineMemOperand *> -createMemoryOperand(MachineFunction &Function, MemoryAccessFlags Accesses, std::uint64_t SizeBytes, - Alignment Align, MemoryBase Base, std::int64_t Offset = 0) { - auto Size = MemorySize::fixed(SizeBytes); - if (!Size) - return std::unexpected(Size.error()); - MachineMemOperandDesc Description; - Description.Accesses = Accesses; - Description.Size = *Size; - Description.Align = Align; - Description.Base = std::move(Base); - Description.Offset = Offset; - return Function.createMemOperand(std::move(Description)); -} - -[[nodiscard]] Expected<void> emitCCCopy(MachineIRBuilder &Builder, Register Destination, - Register Source, CallValueTransform Transform) { - const Opcode CopyOpcode = Transform == CallValueTransform::Full - ? opcode(GenericOpcode::COPY) - : targetOpcode(InstructionOpcode::PseudoCC_COPY); - auto Instruction = Builder.buildInstr(CopyOpcode); - if (!Instruction) - return std::unexpected(Instruction.error()); - auto Def = Instruction->addDef(Destination); - if (!Def) - return std::unexpected(Def.error()); - auto Use = Instruction->addUse(Source); - if (!Use) - return std::unexpected(Use.error()); - if (Transform != CallValueTransform::Full) { - auto Action = Instruction->addImmediate(static_cast<std::int64_t>(Transform)); - if (!Action) - return std::unexpected(Action.error()); - } - auto Built = Instruction->build(); - if (!Built) - return std::unexpected(Built.error()); - return {}; -} - -[[nodiscard]] Expected<void> emitStackLoad(MachineIRBuilder &Builder, - const CCAssignment &Assignment, FrameIndex Object, - std::uint64_t ObjectOffset, - const MachineMemOperand &Memory) { - auto Instruction = Builder.buildInstr(targetOpcode(InstructionOpcode::PseudoLOAD_STACK_ARG)); - if (!Instruction) - return std::unexpected(Instruction.error()); - auto Def = Instruction->addDef(Assignment.OriginalValue); - if (!Def) - return std::unexpected(Def.error()); - auto Frame = Instruction->addFrameIndex(Object); - if (!Frame) - return std::unexpected(Frame.error()); - auto Offset = addUnsignedImmediate(*Instruction, ObjectOffset); - if (!Offset) - return std::unexpected(Offset.error()); - auto Transform = - Instruction->addImmediate(static_cast<std::int64_t>(incomingTransform(Assignment))); - if (!Transform) - return std::unexpected(Transform.error()); - auto Mem = Instruction->addMemOperand(Memory); - if (!Mem) - return std::unexpected(Mem.error()); - auto Built = Instruction->build(); - if (!Built) - return std::unexpected(Built.error()); - return {}; -} - -[[nodiscard]] Expected<void> emitStackStore(MachineIRBuilder &Builder, Register Source, - FrameIndex Object, std::uint64_t ObjectOffset, - const MachineMemOperand &Memory, - CallValueTransform Transform) { - auto Instruction = Builder.buildInstr(targetOpcode(InstructionOpcode::PseudoSTORE_STACK_ARG)); - if (!Instruction) - return std::unexpected(Instruction.error()); - auto Use = Instruction->addUse(Source); - if (!Use) - return std::unexpected(Use.error()); - auto Frame = Instruction->addFrameIndex(Object); - if (!Frame) - return std::unexpected(Frame.error()); - auto Offset = addUnsignedImmediate(*Instruction, ObjectOffset); - if (!Offset) - return std::unexpected(Offset.error()); - auto Action = Instruction->addImmediate(static_cast<std::int64_t>(Transform)); - if (!Action) - return std::unexpected(Action.error()); - auto Mem = Instruction->addMemOperand(Memory); - if (!Mem) - return std::unexpected(Mem.error()); - auto Built = Instruction->build(); - if (!Built) - return std::unexpected(Built.error()); - return {}; -} - -[[nodiscard]] Expected<void> ensureFunctionLiveIn(MachineRegisterInfo &Registers, - PhysicalRegister Reg, - std::optional<Register> InitialValue) { - const auto Existing = - std::ranges::find(Registers.liveIns(), Reg, &PhysicalRegisterLiveIn::PhysReg); - if (Existing == Registers.liveIns().end()) - return Registers.addLiveIn(Reg, InitialValue); - if (InitialValue && Existing->InitialVirtualRegister && - Existing->InitialVirtualRegister != InitialValue) { - return makeUnexpected(ErrorCode::VerificationError, - "physical formal register already maps to another virtual live-in"); - } - return {}; -} - -[[nodiscard]] Expected<void> ensureBlockLiveIn(MachineBasicBlock &Block, PhysicalRegister Reg) { - return Block.addLiveIn(Reg, LaneMask::all()); -} - -[[nodiscard]] Expected<void> verifyPlanRegisters(const CallLoweringPlan &Plan, - const MachineFunction &Function, - const TargetDescription &Target) { - const MachineRegisterInfo &Registers = Function.registerInfo(); - const auto Verify = [&Registers, &Target](const CCAssignment &Assignment) -> Expected<void> { - if (Assignment.LocationRegister && - !Target.registers().isKnownPhysicalRegister(*Assignment.LocationRegister)) { - return makeUnexpected(ErrorCode::VerificationError, - "call-lowering plan names an unknown ABI location register"); - } - const Register Reg = Assignment.OriginalValue; - if (Reg.isNoRegister()) - return {}; - if (Reg.isPhysical()) { - if (!Target.registers().isKnownPhysicalRegister(*Reg.physicalRegister())) { - return makeUnexpected(ErrorCode::VerificationError, - "call-lowering plan names an unknown physical value register"); - } - return {}; - } - auto Info = Registers.virtualRegisterInfo(Reg); - if (!Info) - return std::unexpected(Info.error()); - if (!Info->get().Type || *Info->get().Type != Assignment.ValueType) { - return makeUnexpected(ErrorCode::VerificationError, - "call-lowering plan register LLT differs from MachineRegisterInfo"); - } - return {}; - }; - for (const CCAssignment &Assignment : Plan.argumentAssignments()) { - auto Result = Verify(Assignment); - if (!Result) - return std::unexpected(Result.error()); - } - for (const CCAssignment &Assignment : Plan.returnAssignments()) { - auto Result = Verify(Assignment); - if (!Result) - return std::unexpected(Result.error()); - } - if (const MachineCallTarget *Callee = Plan.callee(); - Callee != nullptr && Callee->kind() == MachineCallTargetKind::IndirectRegister) { - const Register CalleeReg = Callee->indirectRegister(); - if (CalleeReg.isPhysical()) { - const std::optional<PhysicalRegister> Physical = CalleeReg.physicalRegister(); - if (!Physical || !Target.registers().isKnownPhysicalRegister(*Physical)) { - return makeUnexpected(ErrorCode::VerificationError, - "indirect RISC-V callee names an unknown physical register"); - } - } else { - auto Info = Registers.virtualRegisterInfo(CalleeReg); - if (!Info) - return std::unexpected(Info.error()); - const std::optional<LLT> Type = Info->get().Type; - if (!Type || ((!Type->isPointer() && !Type->isInteger()) || Type->scalarBitWidth() != 64)) { - return makeUnexpected(ErrorCode::VerificationError, - "indirect RISC-V callee requires a 64-bit pointer or integer LLT"); - } - } - } - return {}; -} - -[[nodiscard]] Expected<void> verifyRequiredOpcodes(const MachineIRBuilder &Builder) { - static constexpr std::array Required{ - InstructionOpcode::PseudoCC_COPY, - InstructionOpcode::PseudoLOAD_STACK_ARG, - InstructionOpcode::PseudoSTORE_STACK_ARG, - InstructionOpcode::PseudoBYVAL_COPY, - InstructionOpcode::ADJCALLSTACKDOWN, - InstructionOpcode::ADJCALLSTACKUP, - InstructionOpcode::PseudoCALL, - InstructionOpcode::PseudoTAIL, - InstructionOpcode::PseudoRET, - }; - for (const InstructionOpcode OpcodeValue : Required) { - if (Builder.target().instructionDescriptor(targetOpcode(OpcodeValue)) == nullptr) { - return makeUnexpected(ErrorCode::InternalError, - "RISC-V target description lacks a call-lowering pseudo"); - } - } - return {}; -} - -[[nodiscard]] Expected<void> verifyFormalLiveIns(const CallLoweringPlan &Plan, - const MachineFunction &Function) { - if (Plan.kind() != CallLoweringPlanKind::FormalArguments) - return {}; - - const auto LiveIns = Function.registerInfo().liveIns(); - for (const CCAssignment &Assignment : Plan.argumentAssignments()) { - if (!Assignment.LocationRegister || !Assignment.OriginalValue.isVirtual()) - continue; - const auto Existing = - std::ranges::find(LiveIns, *Assignment.LocationRegister, &PhysicalRegisterLiveIn::PhysReg); - if (Existing != LiveIns.end() && Existing->InitialVirtualRegister && - *Existing->InitialVirtualRegister != Assignment.OriginalValue) { - return makeUnexpected(ErrorCode::VerificationError, - "physical formal register already maps to another virtual live-in"); - } - } - return {}; -} - -[[nodiscard]] Expected<void> preflightPlan(MachineIRBuilder &Builder, - const CallLoweringPlan &Plan) { - auto Convention = riscvConvention(Plan.convention()); - if (!Convention) - return std::unexpected(Convention.error()); - if (Plan.target() != PlanTarget) { - return makeUnexpected(ErrorCode::InvalidArgument, - "call-lowering plan belongs to another target or ABI"); - } - auto InsertPoint = Builder.verifyInsertPoint(); - if (!InsertPoint) - return std::unexpected(InsertPoint.error()); - auto Opcodes = verifyRequiredOpcodes(Builder); - if (!Opcodes) - return std::unexpected(Opcodes.error()); - auto Registers = verifyPlanRegisters(Plan, Builder.function(), Builder.target()); - if (!Registers) - return std::unexpected(Registers.error()); - auto LiveIns = verifyFormalLiveIns(Plan, Builder.function()); - if (!LiveIns) - return std::unexpected(LiveIns.error()); - for (const CCAssignment &Assignment : Plan.argumentAssignments()) { - if (Assignment.StackOffsetBytes && - *Assignment.StackOffsetBytes > - static_cast<std::uint64_t>(std::numeric_limits<std::int64_t>::max())) { - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V stack assignment offset exceeds the frame-index range"); - } - } - for (const ByValCopyAssignment &Copy : Plan.byValCopies()) { - if (Copy.StackOffsetBytes > - static_cast<std::uint64_t>(std::numeric_limits<std::int64_t>::max())) { - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V byval offset exceeds the frame-index range"); - } - } - if (Plan.kind() != CallLoweringPlanKind::Return && Builder.function().frameInfo().isFrozen()) { - return makeUnexpected(ErrorCode::VerificationError, - "call lowering cannot modify a frozen machine frame"); - } - if (Plan.kind() == CallLoweringPlanKind::FormalArguments && - (Builder.function().firstBlock() == nullptr || - Builder.insertionBlock() != Builder.function().firstBlock())) { - return makeUnexpected(ErrorCode::InvalidArgument, - "formal arguments must be lowered in the entry machine block"); - } - return {}; -} - -[[nodiscard]] Expected<void> updateMaximumFrameAlignment(MachineFrameState &State, - Alignment Required) { - if (!State.MaxAlignment || State.MaxAlignment->bytes() < Required.bytes()) - State.MaxAlignment = Required; - return {}; -} - -[[nodiscard]] Expected<void> emitFormalArguments(MachineIRBuilder &Builder, - const CallLoweringPlan &Plan) { - MachineFunction &Function = Builder.function(); - MachineBasicBlock &Entry = *Function.firstBlock(); - MachineRegisterInfo &Registers = Function.registerInfo(); - MachineFrameInfo &Frame = Function.frameInfo(); - Alignment RequiredFrameAlignment; - - for (const CCAssignment &Assignment : Plan.argumentAssignments()) { - if (Assignment.LocationRegister) { - const PhysicalRegister Location = *Assignment.LocationRegister; - const std::optional<Register> Initial = - Assignment.OriginalValue.isVirtual() ? std::optional<Register>(Assignment.OriginalValue) - : std::nullopt; - auto FunctionLiveIn = ensureFunctionLiveIn(Registers, Location, Initial); - if (!FunctionLiveIn) - return std::unexpected(FunctionLiveIn.error()); - auto BlockLiveIn = ensureBlockLiveIn(Entry, Location); - if (!BlockLiveIn) - return std::unexpected(BlockLiveIn.error()); - auto Copy = emitCCCopy(Builder, Assignment.OriginalValue, Register::fromPhysical(Location), - incomingTransform(Assignment)); - if (!Copy) - return std::unexpected(Copy.error()); - continue; - } - - if (!Assignment.StackOffsetBytes) { - return makeUnexpected(ErrorCode::InternalError, - "verified formal stack assignment has no stack offset"); - } - StackObjectDesc Object; - Object.Kind = StackObjectKind::FixedABI; - Object.Size = Assignment.StackSlotSizeBytes; - Object.Align = Assignment.LocationAlignment; - Object.Offset = static_cast<std::int64_t>(*Assignment.StackOffsetBytes); - Object.IsImmutable = true; - Object.TargetFlags = stackObjectFlag(CallStackObjectFlag::IncomingArgument); - auto FrameObject = Frame.createObject(std::move(Object)); - if (!FrameObject) - return std::unexpected(FrameObject.error()); - auto Memory = - createMemoryOperand(Function, {MemoryAccessFlag::Load}, Assignment.LocationSizeBytes, - Assignment.LocationAlignment, MemoryBase::frameIndex(*FrameObject)); - if (!Memory) - return std::unexpected(Memory.error()); - auto Load = emitStackLoad(Builder, Assignment, *FrameObject, 0, **Memory); - if (!Load) - return std::unexpected(Load.error()); - if (RequiredFrameAlignment.bytes() < Assignment.LocationAlignment.bytes()) - RequiredFrameAlignment = Assignment.LocationAlignment; - } - - const VarArgLoweringState &VarArgs = Plan.varArgs(); - if (!VarArgs.IsVariadic) { - if (RequiredFrameAlignment.bytes() == 1) - return {}; - MachineFrameState State = Frame.state(); - auto AlignmentUpdate = updateMaximumFrameAlignment(State, RequiredFrameAlignment); - if (!AlignmentUpdate) - return std::unexpected(AlignmentUpdate.error()); - return Frame.setState(std::move(State)); - } - - auto Align8 = alignment(XLenBytes); - auto Align16 = alignment(ABIStackAlignmentBytes); - if (!Align8 || !Align16) - return makeUnexpected(ErrorCode::InternalError, "RISC-V ABI primitive type facts are invalid"); - - FrameIndex SaveObject; - if (VarArgs.RegisterSaveAreaSizeBytes == 0) { - StackObjectDesc Object; - Object.Kind = StackObjectKind::FixedABI; - Object.Size = XLenBytes; - Object.Align = *Align8; - Object.Offset = VarArgs.RegisterSaveAreaOffsetBytes; - Object.IsImmutable = true; - Object.TargetFlags = stackObjectFlag(CallStackObjectFlag::VarArgSaveArea); - auto Created = Frame.createObject(std::move(Object)); - if (!Created) - return std::unexpected(Created.error()); - SaveObject = *Created; - } else { - StackObjectDesc Object; - Object.Kind = StackObjectKind::FixedABI; - Object.Size = VarArgs.RegisterSaveAreaSizeBytes; - Object.Align = VarArgs.FirstUnallocatedIntegerRegister % 2 == 0 ? *Align16 : *Align8; - Object.Offset = VarArgs.RegisterSaveAreaOffsetBytes; - Object.TargetFlags = stackObjectFlag(CallStackObjectFlag::VarArgSaveArea); - auto Created = Frame.createObject(std::move(Object)); - if (!Created) - return std::unexpected(Created.error()); - SaveObject = *Created; - - if (VarArgs.RegisterSavePaddingBytes != 0) { - StackObjectDesc Padding; - Padding.Kind = StackObjectKind::FixedABI; - Padding.Size = VarArgs.RegisterSavePaddingBytes; - Padding.Align = *Align8; - Padding.Offset = VarArgs.RegisterSaveAreaOffsetBytes - - static_cast<std::int64_t>(VarArgs.RegisterSavePaddingBytes); - Padding.IsImmutable = true; - Padding.TargetFlags = stackObjectFlag(CallStackObjectFlag::VarArgAlignmentPadding); - auto CreatedPadding = Frame.createObject(std::move(Padding)); - if (!CreatedPadding) - return std::unexpected(CreatedPadding.error()); - } - } - - for (const VarArgRegisterSave Save : VarArgs.RegistersToSave) { - auto FunctionLiveIn = ensureFunctionLiveIn(Registers, Save.Reg, std::nullopt); - if (!FunctionLiveIn) - return std::unexpected(FunctionLiveIn.error()); - auto BlockLiveIn = ensureBlockLiveIn(Entry, Save.Reg); - if (!BlockLiveIn) - return std::unexpected(BlockLiveIn.error()); - auto Memory = createMemoryOperand(Function, {MemoryAccessFlag::Store}, XLenBytes, *Align8, - MemoryBase::frameIndex(SaveObject), - static_cast<std::int64_t>(Save.OffsetBytes)); - if (!Memory) - return std::unexpected(Memory.error()); - auto Store = emitStackStore(Builder, Register::fromPhysical(Save.Reg), SaveObject, - Save.OffsetBytes, **Memory, CallValueTransform::Full); - if (!Store) - return std::unexpected(Store.error()); - } - - MachineFrameState State = Frame.state(); - const Alignment VarArgAlignment = - RequiredFrameAlignment.bytes() > Align16->bytes() ? RequiredFrameAlignment : *Align16; - auto AlignmentUpdate = updateMaximumFrameAlignment(State, VarArgAlignment); - if (!AlignmentUpdate) - return std::unexpected(AlignmentUpdate.error()); - return Frame.setState(std::move(State)); -} - -[[nodiscard]] Expected<void> emitCallFrameAdjustment(MachineIRBuilder &Builder, - InstructionOpcode OpcodeValue, - const CallFrameRequirements &Frame) { - if (Frame.SizeBytes > static_cast<std::uint64_t>(std::numeric_limits<std::int64_t>::max())) { - return makeUnexpected(ErrorCode::Unsupported, - "RISC-V call-frame size does not fit a MIR immediate"); - } - auto Instruction = Builder.buildInstr(targetOpcode(OpcodeValue)); - if (!Instruction) - return std::unexpected(Instruction.error()); - auto Size = Instruction->addImmediate(static_cast<std::int64_t>(Frame.SizeBytes)); - if (!Size) - return std::unexpected(Size.error()); - auto PreparedBytes = Instruction->addImmediate(0); - if (!PreparedBytes) - return std::unexpected(PreparedBytes.error()); - auto Built = Instruction->build(); - if (!Built) - return std::unexpected(Built.error()); - return {}; -} - -[[nodiscard]] Expected<std::unordered_map<std::size_t, Register>> -emitByValCopies(MachineIRBuilder &Builder, const CallLoweringPlan &Plan) { - MachineFunction &Function = Builder.function(); - MachineFrameInfo &Frame = Function.frameInfo(); - std::unordered_map<std::size_t, Register> Replacements; - for (const ByValCopyAssignment Copy : Plan.byValCopies()) { - StackObjectDesc Object; - Object.Kind = StackObjectKind::Target; - Object.Size = Copy.SizeBytes; - Object.Align = Copy.Align; - Object.LocalOffset = static_cast<std::int64_t>(Copy.StackOffsetBytes); - Object.TargetFlags = stackObjectFlag(CallStackObjectFlag::ByValCopy); - auto FrameObject = Frame.createObject(std::move(Object)); - if (!FrameObject) - return std::unexpected(FrameObject.error()); - auto Temporary = Function.registerInfo().createVirtualRegister(Copy.PointerType); - if (!Temporary) - return std::unexpected(Temporary.error()); - - auto SourceMemory = createMemoryOperand(Function, {MemoryAccessFlag::Load}, Copy.SizeBytes, - Copy.Align, MemoryBase{}); - if (!SourceMemory) - return std::unexpected(SourceMemory.error()); - auto DestinationMemory = - createMemoryOperand(Function, {MemoryAccessFlag::Store}, Copy.SizeBytes, Copy.Align, - MemoryBase::frameIndex(*FrameObject)); - if (!DestinationMemory) - return std::unexpected(DestinationMemory.error()); - - auto Instruction = Builder.buildInstr(targetOpcode(InstructionOpcode::PseudoBYVAL_COPY)); - if (!Instruction) - return std::unexpected(Instruction.error()); - auto Def = Instruction->addDef(*Temporary); - if (!Def) - return std::unexpected(Def.error()); - auto Use = Instruction->addUse(Copy.Source); - if (!Use) - return std::unexpected(Use.error()); - auto FrameOperand = Instruction->addFrameIndex(*FrameObject); - if (!FrameOperand) - return std::unexpected(FrameOperand.error()); - auto Size = addUnsignedImmediate(*Instruction, Copy.SizeBytes); - if (!Size) - return std::unexpected(Size.error()); - auto Align = addUnsignedImmediate(*Instruction, Copy.Align.bytes()); - if (!Align) - return std::unexpected(Align.error()); - auto SourceMem = Instruction->addMemOperand(**SourceMemory); - if (!SourceMem) - return std::unexpected(SourceMem.error()); - auto DestinationMem = Instruction->addMemOperand(**DestinationMemory); - if (!DestinationMem) - return std::unexpected(DestinationMem.error()); - auto Built = Instruction->build(); - if (!Built) - return std::unexpected(Built.error()); - Replacements.emplace(Copy.ArgumentAssignmentIndex, *Temporary); - } - return Replacements; -} - -[[nodiscard]] Expected<void> -emitOutgoingArguments(MachineIRBuilder &Builder, const CallLoweringPlan &Plan, - const std::unordered_map<std::size_t, Register> &Replacements) { - MachineFunction &Function = Builder.function(); - MachineFrameInfo &Frame = Function.frameInfo(); - const auto Assignments = Plan.argumentAssignments(); - for (std::size_t Index = 0; Index < Assignments.size(); ++Index) { - const CCAssignment &Assignment = Assignments[Index]; - const auto Replacement = Replacements.find(Index); - const Register Source = - Replacement == Replacements.end() ? Assignment.OriginalValue : Replacement->second; - if (Assignment.LocationRegister) { - auto Copy = emitCCCopy(Builder, Register::fromPhysical(*Assignment.LocationRegister), Source, - outgoingTransform(Assignment)); - if (!Copy) - return std::unexpected(Copy.error()); - continue; - } - - if (!Assignment.StackOffsetBytes) { - return makeUnexpected(ErrorCode::InternalError, - "verified outgoing stack assignment has no stack offset"); - } - StackObjectDesc Object; - Object.Size = Assignment.StackSlotSizeBytes; - Object.Align = Assignment.LocationAlignment; - Object.TargetFlags = stackObjectFlag(CallStackObjectFlag::OutgoingArgument); - if (Plan.tailDecision() == TailCallKind::NotTail) { - Object.Kind = StackObjectKind::Target; - Object.LocalOffset = static_cast<std::int64_t>(*Assignment.StackOffsetBytes); - } else { - Object.Kind = StackObjectKind::FixedABI; - Object.Offset = static_cast<std::int64_t>(*Assignment.StackOffsetBytes); - } - auto FrameObject = Frame.createObject(std::move(Object)); - if (!FrameObject) - return std::unexpected(FrameObject.error()); - auto Memory = - createMemoryOperand(Function, {MemoryAccessFlag::Store}, Assignment.LocationSizeBytes, - Assignment.LocationAlignment, MemoryBase::frameIndex(*FrameObject)); - if (!Memory) - return std::unexpected(Memory.error()); - auto Store = - emitStackStore(Builder, Source, *FrameObject, 0, **Memory, outgoingTransform(Assignment)); - if (!Store) - return std::unexpected(Store.error()); - } - return {}; -} - -[[nodiscard]] Expected<void> addCallTarget(MachineInstrBuilder &Instruction, - const MachineCallTarget &Callee) { - switch (Callee.kind()) { - case MachineCallTargetKind::GlobalSymbol: - return Instruction.addGlobalSymbol(std::string(Callee.symbolName()), 0, Callee.targetFlags()); - case MachineCallTargetKind::ExternalSymbol: - return Instruction.addExternalSymbol(std::string(Callee.symbolName()), 0, Callee.targetFlags()); - case MachineCallTargetKind::IndirectRegister: - return Instruction.addUse(Callee.indirectRegister()); - case MachineCallTargetKind::Invalid: - break; - } - return makeUnexpected(ErrorCode::InternalError, - "verified call-lowering plan has an invalid callee"); -} - -[[nodiscard]] Expected<PhysicalRegister> requiredLocationRegister(const CCAssignment &Assignment) { - if (!Assignment.LocationRegister) { - return makeUnexpected(ErrorCode::InternalError, - "verified return assignment has no location register"); - } - return *Assignment.LocationRegister; -} - -[[nodiscard]] Expected<void> emitCallInstruction(MachineIRBuilder &Builder, - const CallLoweringPlan &Plan) { - const bool IsTail = Plan.tailDecision() != TailCallKind::NotTail; - auto Instruction = Builder.buildInstr( - targetOpcode(IsTail ? InstructionOpcode::PseudoTAIL : InstructionOpcode::PseudoCALL)); - if (!Instruction) - return std::unexpected(Instruction.error()); - auto Callee = addCallTarget(*Instruction, *Plan.callee()); - if (!Callee) - return std::unexpected(Callee.error()); - auto Mask = Instruction->addRegisterMask(Plan.preservedMask()); - if (!Mask) - return std::unexpected(Mask.error()); - - std::unordered_set<PhysicalRegister> DynamicUses; - for (const CCAssignment &Assignment : Plan.argumentAssignments()) { - if (Assignment.LocationRegister && DynamicUses.insert(*Assignment.LocationRegister).second) { - auto Use = Instruction->addImplicitUse(Register::fromPhysical(*Assignment.LocationRegister)); - if (!Use) - return std::unexpected(Use.error()); - } - } - std::unordered_set<PhysicalRegister> DynamicDefs; - if (!IsTail) { - for (const CCAssignment &Assignment : Plan.returnAssignments()) { - if (Assignment.LocationRegister && DynamicDefs.insert(*Assignment.LocationRegister).second) { - auto Def = - Instruction->addImplicitDef(Register::fromPhysical(*Assignment.LocationRegister)); - if (!Def) - return std::unexpected(Def.error()); - } - } - } - auto Built = Instruction->build(); - if (!Built) - return std::unexpected(Built.error()); - return {}; -} - -[[nodiscard]] Expected<void> emitCall(MachineIRBuilder &Builder, const CallLoweringPlan &Plan) { - if (Plan.callFrame().EmitSetup) { - auto Down = - emitCallFrameAdjustment(Builder, InstructionOpcode::ADJCALLSTACKDOWN, Plan.callFrame()); - if (!Down) - return std::unexpected(Down.error()); - } - - auto Replacements = emitByValCopies(Builder, Plan); - if (!Replacements) - return std::unexpected(Replacements.error()); - auto Arguments = emitOutgoingArguments(Builder, Plan, *Replacements); - if (!Arguments) - return std::unexpected(Arguments.error()); - auto Call = emitCallInstruction(Builder, Plan); - if (!Call) - return std::unexpected(Call.error()); - - if (Plan.callFrame().EmitDestroy) { - auto Up = emitCallFrameAdjustment(Builder, InstructionOpcode::ADJCALLSTACKUP, Plan.callFrame()); - if (!Up) - return std::unexpected(Up.error()); - } - if (Plan.tailDecision() == TailCallKind::NotTail) { - for (const CCAssignment &Assignment : Plan.returnAssignments()) { - auto Location = requiredLocationRegister(Assignment); - if (!Location) - return std::unexpected(Location.error()); - auto Copy = emitCCCopy(Builder, Assignment.OriginalValue, Register::fromPhysical(*Location), - incomingTransform(Assignment)); - if (!Copy) - return std::unexpected(Copy.error()); - } - } - - MachineFrameInfo &Frame = Builder.function().frameInfo(); - MachineFrameState State = Frame.state(); - State.HasCalls = true; - State.HasTailCall = State.HasTailCall || Plan.tailDecision() != TailCallKind::NotTail; - State.AdjustsStack = - State.AdjustsStack || (Plan.callFrame().EmitSetup && Plan.callFrame().SizeBytes != 0); - State.HasMustTailInVarArgFunc = - State.HasMustTailInVarArgFunc || - (Plan.tailDecision() == TailCallKind::MustTail && Plan.varArgs().IsVariadic); - if (Plan.tailDecision() == TailCallKind::NotTail) { - State.MaxCallFrameSize = - std::max(State.MaxCallFrameSize.value_or(0), Plan.callFrame().SizeBytes); - } - auto AlignmentUpdate = updateMaximumFrameAlignment(State, Plan.callFrame().Align); - if (!AlignmentUpdate) - return std::unexpected(AlignmentUpdate.error()); - return Frame.setState(std::move(State)); -} - -[[nodiscard]] Expected<void> emitReturn(MachineIRBuilder &Builder, const CallLoweringPlan &Plan) { - for (const CCAssignment &Assignment : Plan.returnAssignments()) { - auto Location = requiredLocationRegister(Assignment); - if (!Location) - return std::unexpected(Location.error()); - auto Copy = emitCCCopy(Builder, Register::fromPhysical(*Location), Assignment.OriginalValue, - outgoingTransform(Assignment)); - if (!Copy) - return std::unexpected(Copy.error()); - } - - auto Return = Builder.buildInstr(targetOpcode(InstructionOpcode::PseudoRET)); - if (!Return) - return std::unexpected(Return.error()); - for (const CCAssignment &Assignment : Plan.returnAssignments()) { - auto Location = requiredLocationRegister(Assignment); - if (!Location) - return std::unexpected(Location.error()); - auto Use = Return->addImplicitUse(Register::fromPhysical(*Location)); - if (!Use) - return std::unexpected(Use.error()); - } - auto Built = Return->build(); - if (!Built) - return std::unexpected(Built.error()); - return {}; -} - -Expected<void> RISCVCallLowering::emitPlan(MachineIRBuilder &Builder, - const CallLoweringPlan &Plan) const { - auto Preflight = preflightPlan(Builder, Plan); - if (!Preflight) - return std::unexpected(Preflight.error()); - switch (Plan.kind()) { - case CallLoweringPlanKind::FormalArguments: - return emitFormalArguments(Builder, Plan); - case CallLoweringPlanKind::Call: - return emitCall(Builder, Plan); - case CallLoweringPlanKind::Return: - return emitReturn(Builder, Plan); - } - return makeUnexpected(ErrorCode::InternalError, "call-lowering plan kind is invalid"); -} - -} // namespace - -const TargetCallLowering &callLowering() noexcept { - static const RISCVCallLowering Lowering; - return Lowering; -} - -} // namespace rcc::riscv |
