// SPDX-License-Identifier: GPL-3.0-or-later #ifndef RCC_CODEGEN_TARGETCONSTANT_H #define RCC_CODEGEN_TARGETCONSTANT_H #include "rcc/CodeGen/MachineIRTypes.h" #include "rcc/CodeGen/MachineMemOperand.h" #include "rcc/Support/Error.h" #include #include #include #include #include #include #include #include #include #include namespace rcc { struct TargetConstantFixup final { std::uint64_t Offset = 0; TargetRelocationKindID Kind; std::string Symbol; std::int64_t Addend = 0; friend bool operator==(const TargetConstantFixup &Left, const TargetConstantFixup &Right) = default; }; struct TargetConstantEmission final { std::vector Bytes; std::vector Fixups; [[nodiscard]] Expected verify() const; friend bool operator==(const TargetConstantEmission &Left, const TargetConstantEmission &Right) = default; }; struct EncodedTargetConstant final { TargetConstantTypeID TypeID; std::uint32_t Version = 0; std::vector Payload; friend bool operator==(const EncodedTargetConstant &Left, const EncodedTargetConstant &Right) = default; }; template concept TargetConstantValue = std::move_constructible && requires(const ValueT &Value, const ValueT &Other, std::uint32_t Version, std::span Payload, std::string_view Text) { { ValueT::stableTypeID() } -> std::same_as; { ValueT::schemaVersion() } -> std::same_as; { Value.verify() } -> std::same_as>; { Value.clone() } -> std::same_as>; { Value.stableHash() } -> std::same_as; { Value.print() } -> std::same_as; { Value.encode() } -> std::same_as>>; { Value.emit() } -> std::same_as>; { Value.requiredAlignment() } -> std::same_as; { Value.relocationKind() } -> std::same_as>; { Value == Other } -> std::convertible_to; { ValueT::parse(Text) } -> std::same_as>; { ValueT::decode(Version, Payload) } -> std::same_as>; }; class TargetConstant final { public: TargetConstant() = default; TargetConstant(const TargetConstant &) = delete; TargetConstant &operator=(const TargetConstant &) = delete; TargetConstant(TargetConstant &&) noexcept; TargetConstant &operator=(TargetConstant &&) noexcept; ~TargetConstant(); template [[nodiscard]] static Expected create(ValueT Value) { if (ValueT::stableTypeID().value() == 0) return makeUnexpected(ErrorCode::InvalidArgument, "target constant TypeID must be nonzero"); if (ValueT::schemaVersion() == 0) { return makeUnexpected(ErrorCode::InvalidArgument, "target constant schema version must be nonzero"); } auto Verification = Value.verify(); if (!Verification) return std::unexpected(Verification.error()); std::unique_ptr NewStorage = std::make_unique>(std::move(Value)); return TargetConstant(std::move(NewStorage)); } template [[nodiscard]] static Expected parse(std::string_view Text) { auto Value = ValueT::parse(Text); if (!Value) return std::unexpected(Value.error()); return create(std::move(*Value)); } template [[nodiscard]] static Expected decode(const EncodedTargetConstant &Encoded) { if (Encoded.TypeID != ValueT::stableTypeID()) { return makeUnexpected(ErrorCode::ParseError, "encoded target constant TypeID does not match its target schema"); } if (Encoded.Version == 0) return makeUnexpected(ErrorCode::ParseError, "target constant version must be nonzero"); auto Value = ValueT::decode(Encoded.Version, Encoded.Payload); if (!Value) return std::unexpected(Value.error()); return create(std::move(*Value)); } [[nodiscard]] bool hasValue() const noexcept { return Storage != nullptr; } [[nodiscard]] std::optional typeID() const noexcept; [[nodiscard]] std::optional schemaVersion() const noexcept; [[nodiscard]] Expected verify() const; [[nodiscard]] Expected clone() const; [[nodiscard]] Expected stableHash() const; [[nodiscard]] Expected print() const; [[nodiscard]] Expected encode() const; [[nodiscard]] Expected emit() const; [[nodiscard]] Expected requiredAlignment() const; [[nodiscard]] Expected> relocationKind() const; [[nodiscard]] bool equals(const TargetConstant &Other) const noexcept; template [[nodiscard]] ValueT *getIf() noexcept { if (!hasType()) return nullptr; return &static_cast *>(Storage.get())->value(); } template [[nodiscard]] const ValueT *getIf() const noexcept { if (!hasType()) return nullptr; return &static_cast *>(Storage.get())->value(); } private: class Concept { public: Concept() = default; Concept(const Concept &) = delete; Concept &operator=(const Concept &) = delete; Concept(Concept &&) = delete; Concept &operator=(Concept &&) = delete; virtual ~Concept() = default; [[nodiscard]] virtual TargetConstantTypeID typeID() const noexcept = 0; [[nodiscard]] virtual std::uint32_t schemaVersion() const noexcept = 0; [[nodiscard]] virtual const void *localTypeToken() const noexcept = 0; [[nodiscard]] virtual Expected verify() const = 0; [[nodiscard]] virtual Expected> cloneStorage() const = 0; [[nodiscard]] virtual std::uint64_t stableHash() const noexcept = 0; [[nodiscard]] virtual std::string print() const = 0; [[nodiscard]] virtual Expected> encodePayload() const = 0; [[nodiscard]] virtual Expected emit() const = 0; [[nodiscard]] virtual Alignment requiredAlignment() const noexcept = 0; [[nodiscard]] virtual std::optional relocationKind() const noexcept = 0; [[nodiscard]] virtual bool equals(const Concept &Other) const noexcept = 0; }; template [[nodiscard]] static const void *typeToken() noexcept { static const std::byte Token{}; return &Token; } template class Model final : public Concept { public: explicit Model(ValueT Value) : Value(std::move(Value)) {} [[nodiscard]] TargetConstantTypeID typeID() const noexcept override { return ValueT::stableTypeID(); } [[nodiscard]] std::uint32_t schemaVersion() const noexcept override { return ValueT::schemaVersion(); } [[nodiscard]] const void *localTypeToken() const noexcept override { return TargetConstant::typeToken(); } [[nodiscard]] Expected verify() const override { return Value.verify(); } [[nodiscard]] Expected> cloneStorage() const override { auto Cloned = Value.clone(); if (!Cloned) return std::unexpected(Cloned.error()); std::unique_ptr Result = std::make_unique>(std::move(*Cloned)); return Result; } [[nodiscard]] std::uint64_t stableHash() const noexcept override { return Value.stableHash(); } [[nodiscard]] std::string print() const override { return Value.print(); } [[nodiscard]] Expected> encodePayload() const override { return Value.encode(); } [[nodiscard]] Expected emit() const override { return Value.emit(); } [[nodiscard]] Alignment requiredAlignment() const noexcept override { return Value.requiredAlignment(); } [[nodiscard]] std::optional relocationKind() const noexcept override { return Value.relocationKind(); } [[nodiscard]] bool equals(const Concept &Other) const noexcept override { if (Other.localTypeToken() != TargetConstant::typeToken()) return false; return Value == static_cast &>(Other).Value; } [[nodiscard]] ValueT &value() noexcept { return Value; } [[nodiscard]] const ValueT &value() const noexcept { return Value; } private: ValueT Value; }; explicit TargetConstant(std::unique_ptr Storage) : Storage(std::move(Storage)) {} template [[nodiscard]] bool hasType() const noexcept { return Storage != nullptr && Storage->typeID() == ValueT::stableTypeID() && Storage->localTypeToken() == typeToken(); } std::unique_ptr Storage; }; } // namespace rcc #endif // RCC_CODEGEN_TARGETCONSTANT_H