/** ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** * Copyright 2021 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #include "xenia/cpu/hir/hir_builder.h" #include #include #include #include "xenia/base/assert.h" #include "xenia/base/profiling.h" #include "xenia/cpu/function.h" #include "xenia/cpu/hir/block.h" #include "xenia/cpu/hir/instr.h" #include "xenia/cpu/hir/label.h" #include "xenia/cpu/symbol.h" // Will scribble arena memory to hopefully find use before clears. // #define SCRIBBLE_ARENA_ON_RESET namespace xe { namespace cpu { namespace hir { #define ASSERT_ADDRESS_TYPE(value) \ assert_true((value->type) == INT32_TYPE || (value->type) == INT64_TYPE) #define ASSERT_CALL_ADDRESS_TYPE(value) \ assert_true((value->type) == INT32_TYPE || (value->type) == INT64_TYPE) #define ASSERT_INTEGER_TYPE(value) \ assert_true((value->type) == INT8_TYPE || (value->type) == INT16_TYPE || \ (value->type) == INT32_TYPE || (value->type) == INT64_TYPE) #define ASSERT_FLOAT_TYPE(value) \ assert_true((value->type) == FLOAT32_TYPE || (value->type) == FLOAT64_TYPE) #define ASSERT_NON_FLOAT_TYPE(value) \ assert_true((value->type) != FLOAT32_TYPE && (value->type) != FLOAT64_TYPE) #define ASSERT_NON_VECTOR_TYPE(value) assert_false((value->type) == VEC128_TYPE) #define ASSERT_VECTOR_TYPE(value) assert_true((value->type) == VEC128_TYPE) #define ASSERT_FLOAT_OR_VECTOR_TYPE(value) \ assert_true((value->type) == FLOAT32_TYPE || \ (value->type) == FLOAT64_TYPE || (value->type) == VEC128_TYPE) #define ASSERT_TYPES_EQUAL(value1, value2) \ assert_true((value1->type) == (value2->type)) HIRBuilder::HIRBuilder() { arena_ = new Arena(); Reset(); } HIRBuilder::~HIRBuilder() { Reset(); delete arena_; } void HIRBuilder::Reset() { attributes_ = 0; next_label_id_ = 0; next_value_ordinal_ = 0; locals_.clear(); block_head_ = block_tail_ = NULL; current_block_ = NULL; #if SCRIBBLE_ARENA_ON_RESET arena_->DebugFill(); #endif arena_->Reset(); } bool HIRBuilder::Finalize() { // Scan blocks in order and add fallthrough branches. These are needed for // analysis passes to work. We may have also added blocks out of order and // need to ensure they fall through in the right order. for (auto block = block_head_; block != NULL; block = block->next) { bool needs_branch = false; if (block->instr_tail) { if (!IsUnconditionalJump(block->instr_tail)) { // Add tail branch to block that falls through. needs_branch = true; } } else { // Add tail branch to block with no instructions. // Hopefully an optimization pass will clean this up later. needs_branch = true; } if (needs_branch) { current_block_ = block; if (!block->next) { // No following block. // Sometimes VC++ generates functions with bl at the end even if they // will never return. Just add a return to satisfy things. // XELOGW("Fall-through out of the function."); Trap(); Return(); current_block_ = NULL; break; } // Add branch. Branch(block->next, BRANCH_LIKELY); current_block_ = NULL; } } return true; } void HIRBuilder::DumpValue(StringBuffer* str, Value* value) { if (value->IsConstant()) { switch (value->type) { case INT8_TYPE: str->AppendFormat("{:X}", value->constant.i8); break; case INT16_TYPE: str->AppendFormat("{:X}", value->constant.i16); break; case INT32_TYPE: str->AppendFormat("{:X}", value->constant.i32); break; case INT64_TYPE: str->AppendFormat("{:X}", value->constant.i64); break; case FLOAT32_TYPE: str->AppendFormat("{:F}", value->constant.f32); break; case FLOAT64_TYPE: str->AppendFormat("{:F}", value->constant.f64); break; case VEC128_TYPE: str->AppendFormat("({:F},{:F},{:F},{:F})", value->constant.v128.x, value->constant.v128.y, value->constant.v128.z, value->constant.v128.w); break; default: assert_always(); break; } } else { static const char* type_names[] = { "i8", "i16", "i32", "i64", "f32", "f64", "v128", }; str->AppendFormat("v{}.{}", value->ordinal, type_names[value->type]); } if (value->reg.index != -1) { str->AppendFormat("<{}{}>", value->reg.set->name, value->reg.index); } } void HIRBuilder::DumpOp(StringBuffer* str, OpcodeSignatureType sig_type, Instr::Op* op) { switch (sig_type) { case OPCODE_SIG_TYPE_X: break; case OPCODE_SIG_TYPE_L: if (op->label->name) { str->Append(op->label->name); } else { str->AppendFormat("label{}", op->label->id); } break; case OPCODE_SIG_TYPE_O: str->AppendFormat("+{}", op->offset); break; case OPCODE_SIG_TYPE_S: if (true) { auto target = op->symbol; str->Append(!target->name().empty() ? target->name() : ""); } break; case OPCODE_SIG_TYPE_V: DumpValue(str, op->value); break; } } void HIRBuilder::Dump(StringBuffer* str) { if (attributes_) { str->AppendFormat("; attributes = {:08X}\n", attributes_); } for (auto it = locals_.begin(); it != locals_.end(); ++it) { auto local = *it; str->Append(" ; local "); DumpValue(str, local); str->Append('\n'); } uint32_t block_ordinal = 0; Block* block = block_head_; while (block) { if (block == block_head_) { str->Append(":\n"); } else if (!block->label_head) { str->AppendFormat(":\n", block_ordinal); } block_ordinal++; Label* label = block->label_head; while (label) { if (label->name) { str->AppendFormat("{}:\n", label->name); } else { str->AppendFormat("label{}:\n", label->id); } label = label->next; } Edge* incoming_edge = block->incoming_edge_head; while (incoming_edge) { auto src_label = incoming_edge->src->label_head; if (src_label && src_label->name) { str->AppendFormat(" ; in: {}", src_label->name); } else if (src_label) { str->AppendFormat(" ; in: label{}", src_label->id); } else { str->AppendFormat(" ; in: ", incoming_edge->src->ordinal); } str->AppendFormat(", dom:{}, uncond:{}\n", (incoming_edge->flags & Edge::DOMINATES) ? 1 : 0, (incoming_edge->flags & Edge::UNCONDITIONAL) ? 1 : 0); incoming_edge = incoming_edge->incoming_next; } Edge* outgoing_edge = block->outgoing_edge_head; while (outgoing_edge) { auto dest_label = outgoing_edge->dest->label_head; if (dest_label && dest_label->name) { str->AppendFormat(" ; out: {}", dest_label->name); } else if (dest_label) { str->AppendFormat(" ; out: label{}", dest_label->id); } else { str->AppendFormat(" ; out: ", outgoing_edge->dest->ordinal); } str->AppendFormat(", dom:{}, uncond:{}\n", (outgoing_edge->flags & Edge::DOMINATES) ? 1 : 0, (outgoing_edge->flags & Edge::UNCONDITIONAL) ? 1 : 0); outgoing_edge = outgoing_edge->outgoing_next; } Instr* i = block->instr_head; while (i) { if (i->opcode->flags & OPCODE_FLAG_HIDE) { i = i->next; continue; } if (i->opcode == &OPCODE_COMMENT_info) { str->AppendFormat(" ; {}\n", reinterpret_cast(i->src1.offset)); i = i->next; continue; } const OpcodeInfo* info = i->opcode; OpcodeSignatureType dest_type = GET_OPCODE_SIG_TYPE_DEST(info->signature); OpcodeSignatureType src1_type = GET_OPCODE_SIG_TYPE_SRC1(info->signature); OpcodeSignatureType src2_type = GET_OPCODE_SIG_TYPE_SRC2(info->signature); OpcodeSignatureType src3_type = GET_OPCODE_SIG_TYPE_SRC3(info->signature); str->Append(" "); if (dest_type) { DumpValue(str, i->dest); str->Append(" = "); } if (i->flags) { str->AppendFormat("{}.{}", GetOpcodeName(info), i->flags); } else { str->Append(GetOpcodeName(info)); } if (src1_type) { str->Append(' '); DumpOp(str, src1_type, &i->src1); } if (src2_type) { str->Append(", "); DumpOp(str, src2_type, &i->src2); } if (src3_type) { str->Append(", "); DumpOp(str, src3_type, &i->src3); } str->Append('\n'); i = i->next; } block = block->next; } } void HIRBuilder::AssertNoCycles() { Block* hare = block_head_; Block* tortoise = block_head_; if (!hare) { return; } while ((hare = hare->next)) { if (hare == tortoise) { // Cycle! assert_always(); } hare = hare->next; if (hare == tortoise) { // Cycle! assert_always(); } tortoise = tortoise->next; if (!hare || !tortoise) { return; } } } Block* HIRBuilder::current_block() const { return current_block_; } Instr* HIRBuilder::last_instr() const { if (current_block_ && current_block_->instr_tail) { return current_block_->instr_tail; } else if (block_tail_) { return block_tail_->instr_tail; } return NULL; } Label* HIRBuilder::NewLabel() { Label* label = arena_->Alloc