#pragma once #include "prelude/string.hpp" #include "ir/value.hpp" #include "ir/op.hpp" #include "ir/block.hpp" namespace IR { class ExternOp : public Op { public: ExternOp(StringView symbol) : m_symbol(symbol) {} ~ExternOp() {} OP_TYPE(EXTERN) public: StringView Format(int indent) const override { StringBuilder sb; sb.AppendIndent(indent); sb << "extern " << m_symbol.c_str(); return sb.view(); } public: const StringView &symbol() const { return m_symbol; } private: StringView m_symbol; }; // TODO: Make function return value (i.e. inherit the OpValued class instead) class FnOp : public Op { public: FnOp(StringView name, const View ¶ms, Block &&block) : m_name(name), m_params(params), m_body(block) {} ~FnOp() {} OP_TYPE(FN) public: StringView Format(int indent) const override { StringBuilder sb; sb.AppendIndent(indent); sb << "label " << m_name.c_str() << ':' << '\n'; sb << m_body.Format(indent); return sb.view(); } public: const StringView &name() const { return m_name; } const Block &body() const { return m_body; } const View ¶ms() const { return m_params; } private: StringView m_name; View m_params; Block m_body; }; // Allocate slot on the stack for variable // with the size of destination value, // aka (dest.GetSize() will be used) class AllocateOp : public OpValued { public: AllocateOp(ValueHandle *dest, const ValueHandle::Type *typ) : OpValued(dest), m_typ(new ValueHandle::Type(typ)) {} ~AllocateOp() {} OP_TYPE(ALLOCATE) public: StringView Format(int indent) const override { StringBuilder sb; sb.AppendIndent(indent); sb << result()->Format() << " = allocate " << m_typ->Format(); return sb.view(); } private: ValueHandle::Type *m_typ; }; class LoadOp : public OpValued { public: LoadOp(ValueHandle *dest, Pointer *ptr) : OpValued(dest), m_ptr(ptr) {} ~LoadOp() {} OP_TYPE(LOAD) public: StringView Format(int indent) const override { StringBuilder sb; sb.AppendIndent(indent); sb << result()->Format() << " = load " << result()->GetType()->Format() << ", " << m_ptr->Format(); return sb.view(); } private: Pointer* m_ptr; }; class StoreOp : public Op { public: StoreOp(ValueHandle *src, Pointer *dst) : m_dst(dst), m_src(src) {} ~StoreOp() {} OP_TYPE(STORE) public: StringView Format(int indent) const override { StringBuilder sb; sb.AppendIndent(indent); sb << "store " << src()->Format() << ", " << dst()->Format(); return sb.view(); } public: const Pointer *dst() const { return m_dst; } const ValueHandle *src() const { return m_src; } private: Pointer *m_dst; ValueHandle *m_src; }; class MathOp : public OpValued { public: MathOp(ValueHandle *dest, ValueHandle *lhs, ValueHandle *rhs, OpType typ) : OpValued(dest), m_lhs(lhs), m_rhs(rhs), m_typ(typ) {} ~MathOp() {} OpType GetType() const override { return m_typ; } private: StringView FormatOperation() const { switch (m_typ) { case OpType::ADD: return StringView("add"); case OpType::MUL: return StringView("mul"); case OpType::SUB: return StringView("sub"); case OpType::DIV: return StringView("div"); default: break; } assert(false && "unreachable"); } public: StringView Format(int indent) const override { StringBuilder sb; sb.AppendIndent(indent); sb << result()->Format() << " = " << FormatOperation() << " " << m_lhs->Format() << ", " << m_rhs->Format(); return sb.view(); } public: ValueHandle *lhs() const { return m_lhs; } ValueHandle *rhs() const { return m_rhs; } private: ValueHandle *m_lhs; ValueHandle *m_rhs; OpType m_typ; }; class CallOp : public OpValued { public: CallOp(ValueHandle *dest, StringView callee, ValueView args) : OpValued(dest), m_callee(callee), m_args(args) {} ~CallOp() {} OP_TYPE(CALL) public: StringView Format(int indent) const override { StringBuilder sb; for (size_t i = 0; i < m_args.size; ++i) { sb.AppendIndent(indent); sb << "param " << m_args.data[i]->Format() << '\n'; } sb.AppendIndent(indent); sb << result()->Format() << " = call " << m_callee.c_str(); return sb.view(); } public: const StringView &callee() const { return m_callee; } const ValueView &args() const { return m_args; } private: StringView m_callee; ValueView m_args; }; } // namespace IR