/* 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_util.cc /// @brief Constant evaluation, array init flattening, array address computation #include "rcc/irgen.h" #include namespace rcc { // Compile-time constant evaluation int32_t IRGen::eval_const_int(ASTNode* node) { switch (node->kind) { case NodeKind::IntLiteral: return static_cast(node)->value; case NodeKind::FloatLiteral: return static_cast(static_cast(node)->value); case NodeKind::LVal: { auto* lv = static_cast(node); Symbol* sym = sym_.lookup(lv->name); if (sym && sym->is_const && !sym->is_array) { if (auto* iv = std::get_if(&sym->const_val)) return *iv; if (auto* fv = std::get_if(&sym->const_val)) return static_cast(*fv); } return 0; } case NodeKind::UnaryExpr: { auto* u = static_cast(node); int32_t v = eval_const_int(u->operand.get()); if (u->op == UnaryOp::Neg) return -v; if (u->op == UnaryOp::Not) return !v; return v; } case NodeKind::BinaryExpr: { auto* b = static_cast(node); int32_t l = eval_const_int(b->lhs.get()); int32_t r = eval_const_int(b->rhs.get()); switch (b->op) { case BinOp::Add: return l + r; case BinOp::Sub: return l - r; case BinOp::Mul: return l * r; case BinOp::Div: return r ? l / r : 0; case BinOp::Mod: return r ? l % r : 0; case BinOp::Lt: return l < r; case BinOp::Gt: return l > r; case BinOp::Le: return l <= r; case BinOp::Ge: return l >= r; case BinOp::Eq: return l == r; case BinOp::Ne: return l != r; case BinOp::And: return l && r; case BinOp::Or: return l || r; } return 0; } default: return 0; } } float IRGen::eval_const_float(ASTNode* node) { switch (node->kind) { case NodeKind::FloatLiteral: return static_cast(node)->value; case NodeKind::IntLiteral: return static_cast(static_cast(node)->value); case NodeKind::LVal: { auto* lv = static_cast(node); Symbol* sym = sym_.lookup(lv->name); if (sym && sym->is_const && !sym->is_array) { if (auto* fv = std::get_if(&sym->const_val)) return *fv; if (auto* iv = std::get_if(&sym->const_val)) return static_cast(*iv); } return 0.0f; } case NodeKind::UnaryExpr: { auto* u = static_cast(node); float v = eval_const_float(u->operand.get()); if (u->op == UnaryOp::Neg) return -v; return v; } case NodeKind::BinaryExpr: { auto* b = static_cast(node); float l = eval_const_float(b->lhs.get()); float r = eval_const_float(b->rhs.get()); switch (b->op) { case BinOp::Add: return l + r; case BinOp::Sub: return l - r; case BinOp::Mul: return l * r; case BinOp::Div: return r != 0.0f ? l / r : 0.0f; default: return 0.0f; } } default: return 0.0f; } } // Array initialization flattening void IRGen::flatten_init(const std::vector& dims, ASTNode* init, std::vector& out) { if (!init) return; if (init->kind != NodeKind::InitList) { out.push_back(init); return; } auto* list = static_cast(init); if (dims.empty()) return; int sub_size = 1; for (size_t i = 1; i < dims.size(); i++) sub_size *= dims[i]; int total = dims[0] * sub_size; int pos = 0; for (auto& item : list->items) { if (pos >= total) break; if (item->kind == NodeKind::InitList) { if (dims.size() > 1) { std::vector sub_dims(dims.begin() + 1, dims.end()); int before = static_cast(out.size()); flatten_init(sub_dims, item.get(), out); int filled = static_cast(out.size()) - before; while (filled < sub_size) { out.push_back(nullptr); filled++; } pos += sub_size; } else { auto* inner = static_cast(item.get()); for (auto& elem : inner->items) { if (pos < total) { out.push_back(elem.get()); pos++; } } } } else { out.push_back(item.get()); pos++; } } while (pos < total) { out.push_back(nullptr); pos++; } } // Array address calculation Ref IRGen::compute_array_addr(LValNode* node) { Symbol* sym = sym_.lookup(node->name); Ref base = get_var_addr_ref(sym); if (sym->is_param && sym->is_array) base = read_variable(sym->uid, cur_blk_, Kl); const auto& dims = sym->dims; size_t ndims = dims.size(); Ref offset = Ref::none(); bool offset_valid = false; for (size_t i = 0; i < node->indices.size(); i++) { Ref idx = visit_expr(node->indices[i].get()); int stride = 1; size_t dim_idx = sym->is_param ? i : (i + 1); for (size_t j = dim_idx; j < ndims; j++) stride *= dims[j]; // Extend index to 'l' type for pointer arithmetic int idx_cls = Kw; if (rtype(idx) == RTmp && idx.val >= Tmp0) idx_cls = cur_fn_->tmp[idx.val].cls; if (idx_cls == Kw) idx = emit_ins(Oextsw, Kl, idx, Ref::none()); Ref byte_off = emit_ins(Omul, Kl, idx, make_int_ref(stride * 4)); if (offset_valid) { offset = emit_ins(Oadd, Kl, offset, byte_off); } else { offset = byte_off; offset_valid = true; } } if (offset_valid) return emit_ins(Oadd, Kl, base, offset); return base; } } // namespace rcc