/* Source code for RCC-Legacy. Copyright (C) 2026 Richard Qin This file is part of RCC. RCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. RCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with RCC; see the file LICENSE. If not see . */ /// @file irgen_expr.cc /// @brief Expression evaluation + short-circuit conditional jump #include "rcc/irgen.h" namespace rcc { int IRGen::ref_cls_of(Ref r, Fn* fn) { if (rtype(r) == RTmp && r.val >= Tmp0) return fn->tmp[r.val].cls; if (rtype(r) == RCon) { Con* c = &fn->con[r.val]; if (c->type == Con::CBits && c->flt) return Ks; } return Kw; } Ref IRGen::coerce(Ref v, int target_cls) { int cur = ref_cls_of(v, cur_fn_); if (cur == target_cls) return v; if (target_cls == Ks && cur == Kw) return emit_ins(Oswtof, Ks, v, Ref::none()); if (target_cls == Kw && cur == Ks) return emit_ins(Ostosi, Kw, v, Ref::none()); return v; } // Expression evaluation Ref IRGen::visit_expr(ASTNode* node) { switch (node->kind) { case NodeKind::IntLiteral: return make_int_ref(static_cast(node)->value); case NodeKind::FloatLiteral: return make_float_ref(static_cast(node)->value); case NodeKind::LVal: { auto* lv = static_cast(node); Symbol* sym = sym_.lookup(lv->name); if (!sym) die("undefined variable"); // Constant scalar: return constant value directly if (sym->is_const && !sym->is_array && lv->indices.empty()) { if (auto* iv = std::get_if(&sym->const_val)) return make_int_ref(*iv); if (auto* fv = std::get_if(&sym->const_val)) return make_float_ref(*fv); } // Array without index -> return address if (sym->is_array && lv->indices.empty()) return get_var_addr_ref(sym); if (sym->is_array && !lv->indices.empty()) { if (lv->indices.size() < sym->dims.size() + (sym->is_param ? 1 : 0)) return compute_array_addr(lv); Ref addr = compute_array_addr(lv); return emit_load(addr, btype_to_cls(sym->type)); } // Global variable if (sym->is_global) { Ref addr = make_global_ref(sym->name.c_str()); return emit_load(addr, btype_to_cls(sym->type)); } // Alloca variable if (sym->alloca_temp_id >= 0) { Ref addr = Ref::tmp(static_cast(sym->alloca_temp_id)); return emit_load(addr, btype_to_cls(sym->type)); } // Normal local scalar: Braun int cls = btype_to_cls(sym->type); return read_variable(sym->uid, cur_blk_, cls); } case NodeKind::BinaryExpr: { auto* bin = static_cast(node); // Logical operation -> short-circuit evaluation -> phi(1,0) if (bin->op == BinOp::And || bin->op == BinOp::Or) { Blk* true_bb = new_blk("cond.true"); Blk* false_bb = new_blk("cond.false"); Blk* merge_bb = new_blk("cond.merge"); visit_cond(node, true_bb, false_bb); seal_block(true_bb); cur_blk_ = true_bb; emit_jmp(merge_bb); seal_block(false_bb); cur_blk_ = false_bb; emit_jmp(merge_bb); seal_block(merge_bb); cur_blk_ = merge_bb; // Phi: true→1, false→0 auto* phi = static_cast(alloc(ctx_, sizeof(Phi))); memset(phi, 0, sizeof(Phi)); phi->cls = Kw; phi->to = new_tmp_ref(Kw); phi->narg = 2; phi->arg = static_cast(alloc(ctx_, 2 * sizeof(Ref))); phi->blk = static_cast(alloc(ctx_, 2 * sizeof(Blk*))); phi->arg[0] = make_int_ref(1); phi->blk[0] = true_bb; phi->arg[1] = make_int_ref(0); phi->blk[1] = false_bb; phi->link = merge_bb->phi; merge_bb->phi = phi; return phi->to; } Ref lhs = visit_expr(bin->lhs.get()); Ref rhs = visit_expr(bin->rhs.get()); int lk = ref_cls_of(lhs, cur_fn_); int rk = ref_cls_of(rhs, cur_fn_); bool is_float = (lk == Ks || rk == Ks); if (is_float && lk == Kw) lhs = emit_ins(Oswtof, Ks, lhs, Ref::none()); if (is_float && rk == Kw) rhs = emit_ins(Oswtof, Ks, rhs, Ref::none()); int rt = is_float ? Ks : Kw; switch (bin->op) { case BinOp::Add: return emit_ins(Oadd, rt, lhs, rhs); case BinOp::Sub: return emit_ins(Osub, rt, lhs, rhs); case BinOp::Mul: return emit_ins(Omul, rt, lhs, rhs); case BinOp::Div: return emit_ins(Odiv, rt, lhs, rhs); case BinOp::Mod: return emit_ins(Orem, rt, lhs, rhs); case BinOp::Lt: return emit_ins(is_float ? Oclts : Ocsltw, Kw, lhs, rhs); case BinOp::Gt: return emit_ins(is_float ? Ocgts : Ocsgtw, Kw, lhs, rhs); case BinOp::Le: return emit_ins(is_float ? Ocles : Ocslew, Kw, lhs, rhs); case BinOp::Ge: return emit_ins(is_float ? Ocges : Ocsgew, Kw, lhs, rhs); case BinOp::Eq: return emit_ins(is_float ? Oceqs : Oceqw, Kw, lhs, rhs); case BinOp::Ne: return emit_ins(is_float ? Ocnes : Ocnew, Kw, lhs, rhs); default: return make_int_ref(0); } } case NodeKind::UnaryExpr: { auto* u = static_cast(node); Ref operand = visit_expr(u->operand.get()); switch (u->op) { case UnaryOp::Pos: return operand; case UnaryOp::Neg: { int k = ref_cls_of(operand, cur_fn_); if (k == Ks) return emit_ins(Oneg, Ks, operand, Ref::none()); return emit_ins(Osub, Kw, make_int_ref(0), operand); } case UnaryOp::Not: return emit_ins(Oceqw, Kw, operand, make_int_ref(0)); } return make_int_ref(0); } case NodeKind::FuncCall: { auto* call_node = static_cast(node); // starttime/stoptime special handling if (call_node->name == "starttime" || call_node->name == "stoptime") { std::string real_name = "_sysy_" + call_node->name; Ref fn_ref = make_global_ref(real_name.c_str()); Ref line_ref = make_int_ref(call_node->lineno); // arg w, line Ins arg_i; arg_i.op = Oarg; arg_i.cls = Kw; arg_i.to = Ref::none(); arg_i.arg[0] = line_ref; arg_i.arg[1] = Ref::none(); push_ins(arg_i); // call Ins call_i; call_i.op = Ocall; call_i.cls = Kw; call_i.to = Ref::none(); call_i.arg[0] = fn_ref; call_i.arg[1] = Ref::none(); push_ins(call_i); return make_int_ref(0); } FuncSig* fsig = sym_.lookup_func(call_node->name); bool is_void = fsig && fsig->ret_type == BType::Void; int ret_cls = fsig ? btype_to_cls(fsig->ret_type) : Kw; std::vector arg_refs; std::vector arg_clses; arg_refs.reserve(call_node->args.size()); arg_clses.reserve(call_node->args.size()); for (size_t i = 0; i < call_node->args.size(); i++) { Ref v = visit_expr(call_node->args[i].get()); int acls = Kw; if (fsig && i < fsig->params.size()) acls = fsig->params[i].second ? Kl : btype_to_cls(fsig->params[i].first); if (acls == Kw || acls == Ks) v = coerce(v, acls); arg_refs.push_back(v); arg_clses.push_back(acls); } for (size_t i = 0; i < arg_refs.size(); i++) { Ins arg_i; arg_i.op = Oarg; arg_i.cls = static_cast(arg_clses[i]); arg_i.to = Ref::none(); arg_i.arg[0] = arg_refs[i]; arg_i.arg[1] = Ref::none(); push_ins(arg_i); } // call instruction Ref fn_ref = make_global_ref(call_node->name.c_str()); if (is_void) { Ins call_i; call_i.op = Ocall; call_i.cls = Kw; call_i.to = Ref::none(); call_i.arg[0] = fn_ref; call_i.arg[1] = Ref::none(); push_ins(call_i); cur_fn_->leaf = 0; return make_int_ref(0); } Ref dst = new_tmp_ref(ret_cls); Ins call_i; call_i.op = Ocall; call_i.cls = static_cast(ret_cls); call_i.to = dst; call_i.arg[0] = fn_ref; call_i.arg[1] = Ref::none(); push_ins(call_i); cur_fn_->leaf = 0; return dst; } case NodeKind::ImplicitCast: { auto* c = static_cast(node); Ref v = visit_expr(c->operand.get()); int k = ref_cls_of(v, cur_fn_); if (c->target_type == BType::Float && k == Kw) return emit_ins(Oswtof, Ks, v, Ref::none()); if (c->target_type == BType::Int && k == Ks) return emit_ins(Ostosi, Kw, v, Ref::none()); return v; } default: return make_int_ref(0); } } // Short-circuit conditional jump void IRGen::visit_cond(ASTNode* cond, Blk* true_bb, Blk* false_bb) { if (auto* bin = dynamic_cast(cond)) { if (bin->op == BinOp::And) return visit_cond_and(bin, true_bb, false_bb); if (bin->op == BinOp::Or) return visit_cond_or(bin, true_bb, false_bb); } Ref val = visit_expr(cond); emit_jnz(val, true_bb, false_bb); } void IRGen::visit_cond_and(BinaryExprNode* node, Blk* true_bb, Blk* false_bb) { Blk* rhs_bb = new_blk("and.rhs"); visit_cond(node->lhs.get(), rhs_bb, false_bb); seal_block(rhs_bb); cur_blk_ = rhs_bb; visit_cond(node->rhs.get(), true_bb, false_bb); } void IRGen::visit_cond_or(BinaryExprNode* node, Blk* true_bb, Blk* false_bb) { Blk* rhs_bb = new_blk("or.rhs"); visit_cond(node->lhs.get(), true_bb, rhs_bb); seal_block(rhs_bb); cur_blk_ = rhs_bb; visit_cond(node->rhs.get(), true_bb, false_bb); } } // namespace rcc