| /* |
| * Copyright (c) 2012-2015 Advanced Micro Devices, Inc. |
| * All rights reserved. |
| * |
| * For use for simulation and test purposes only |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * |
| * 3. Neither the name of the copyright holder nor the names of its contributors |
| * may be used to endorse or promote products derived from this software |
| * without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| * |
| * Author: Steve Reinhardt |
| */ |
| |
| #include "arch/hsail/operand.hh" |
| |
| using namespace Brig; |
| |
| bool |
| BaseRegOperand::init(unsigned opOffset, const BrigObject *obj, |
| unsigned &maxRegIdx, char _regFileChar) |
| { |
| regFileChar = _regFileChar; |
| const BrigOperand *brigOp = obj->getOperand(opOffset); |
| |
| if (brigOp->kind != BRIG_KIND_OPERAND_REGISTER) |
| return false; |
| |
| const BrigOperandRegister *brigRegOp = (const BrigOperandRegister*)brigOp; |
| |
| regIdx = brigRegOp->regNum; |
| |
| DPRINTF(GPUReg, "Operand: regNum: %d, kind: %d\n", regIdx, |
| brigRegOp->regKind); |
| |
| maxRegIdx = std::max(maxRegIdx, regIdx); |
| |
| return true; |
| } |
| |
| void |
| ListOperand::init(unsigned opOffset, const BrigObject *obj) |
| { |
| const BrigOperand *brigOp = (const BrigOperand*)obj->getOperand(opOffset); |
| |
| switch (brigOp->kind) { |
| case BRIG_KIND_OPERAND_CODE_LIST: |
| { |
| const BrigOperandCodeList *opList = |
| (const BrigOperandCodeList*)brigOp; |
| |
| const Brig::BrigData *oprnd_data = |
| obj->getBrigBaseData(opList->elements); |
| |
| // Note: for calls Dest list of operands could be size of 0. |
| elementCount = oprnd_data->byteCount / 4; |
| |
| DPRINTF(GPUReg, "Operand Code List: # elements: %d\n", |
| elementCount); |
| |
| for (int i = 0; i < elementCount; ++i) { |
| unsigned *data_offset = |
| (unsigned*)obj->getData(opList->elements + 4 * (i + 1)); |
| |
| const BrigDirectiveVariable *p = |
| (const BrigDirectiveVariable*)obj-> |
| getCodeSectionEntry(*data_offset); |
| |
| StorageElement *se = obj->currentCode->storageMap-> |
| findSymbol(BRIG_SEGMENT_ARG, p); |
| |
| assert(se); |
| callArgs.push_back(se); |
| } |
| } |
| break; |
| default: |
| fatal("ListOperand: bad operand kind %d\n", brigOp->kind); |
| } |
| } |
| |
| std::string |
| ListOperand::disassemble() |
| { |
| std::string res_str(""); |
| |
| for (auto it : callArgs) { |
| res_str += csprintf("%s ", it->name.c_str()); |
| } |
| |
| return res_str; |
| } |
| |
| void |
| FunctionRefOperand::init(unsigned opOffset, const BrigObject *obj) |
| { |
| const BrigOperand *baseOp = obj->getOperand(opOffset); |
| |
| if (baseOp->kind != BRIG_KIND_OPERAND_CODE_REF) { |
| fatal("FunctionRefOperand: bad operand kind %d\n", baseOp->kind); |
| } |
| |
| const BrigOperandCodeRef *brigOp = (const BrigOperandCodeRef*)baseOp; |
| |
| const BrigDirectiveExecutable *p = |
| (const BrigDirectiveExecutable*)obj->getCodeSectionEntry(brigOp->ref); |
| |
| func_name = obj->getString(p->name); |
| } |
| |
| std::string |
| FunctionRefOperand::disassemble() |
| { |
| DPRINTF(GPUReg, "Operand Func-ref name: %s\n", func_name); |
| |
| return csprintf("%s", func_name); |
| } |
| |
| bool |
| BaseRegOperand::init_from_vect(unsigned opOffset, const BrigObject *obj, |
| int at, unsigned &maxRegIdx, char _regFileChar) |
| { |
| regFileChar = _regFileChar; |
| const BrigOperand *brigOp = obj->getOperand(opOffset); |
| |
| if (brigOp->kind != BRIG_KIND_OPERAND_OPERAND_LIST) |
| return false; |
| |
| |
| const Brig::BrigOperandOperandList *brigRegVecOp = |
| (const Brig::BrigOperandOperandList*)brigOp; |
| |
| unsigned *data_offset = |
| (unsigned*)obj->getData(brigRegVecOp->elements + 4 * (at + 1)); |
| |
| const BrigOperand *p = |
| (const BrigOperand*)obj->getOperand(*data_offset); |
| if (p->kind != BRIG_KIND_OPERAND_REGISTER) { |
| return false; |
| } |
| |
| const BrigOperandRegister *brigRegOp =(const BrigOperandRegister*)p; |
| |
| regIdx = brigRegOp->regNum; |
| |
| DPRINTF(GPUReg, "Operand: regNum: %d, kind: %d \n", regIdx, |
| brigRegOp->regKind); |
| |
| maxRegIdx = std::max(maxRegIdx, regIdx); |
| |
| return true; |
| } |
| |
| void |
| BaseRegOperand::initWithStrOffset(unsigned strOffset, const BrigObject *obj, |
| unsigned &maxRegIdx, char _regFileChar) |
| { |
| const char *name = obj->getString(strOffset); |
| char *endptr; |
| regIdx = strtoul(name + 2, &endptr, 10); |
| |
| if (name[0] != '$' || name[1] != _regFileChar) { |
| fatal("register operand parse error on \"%s\"\n", name); |
| } |
| |
| maxRegIdx = std::max(maxRegIdx, regIdx); |
| } |
| |
| unsigned SRegOperand::maxRegIdx; |
| unsigned DRegOperand::maxRegIdx; |
| unsigned CRegOperand::maxRegIdx; |
| |
| std::string |
| SRegOperand::disassemble() |
| { |
| return csprintf("$s%d", regIdx); |
| } |
| |
| std::string |
| DRegOperand::disassemble() |
| { |
| return csprintf("$d%d", regIdx); |
| } |
| |
| std::string |
| CRegOperand::disassemble() |
| { |
| return csprintf("$c%d", regIdx); |
| } |
| |
| BrigRegOperandInfo |
| findRegDataType(unsigned opOffset, const BrigObject *obj) |
| { |
| const BrigOperand *baseOp = obj->getOperand(opOffset); |
| |
| switch (baseOp->kind) { |
| case BRIG_KIND_OPERAND_REGISTER: |
| { |
| const BrigOperandRegister *op = (BrigOperandRegister*)baseOp; |
| |
| return BrigRegOperandInfo((BrigKind16_t)baseOp->kind, |
| (BrigRegisterKind)op->regKind); |
| } |
| break; |
| |
| case BRIG_KIND_OPERAND_WAVESIZE: |
| { |
| BrigRegisterKind reg_kind = BRIG_REGISTER_KIND_DOUBLE; |
| return BrigRegOperandInfo((BrigKind16_t)baseOp->kind, reg_kind); |
| } |
| |
| case BRIG_KIND_OPERAND_OPERAND_LIST: |
| { |
| const BrigOperandOperandList *op = |
| (BrigOperandOperandList*)baseOp; |
| const BrigData *data_p = (BrigData*)obj->getData(op->elements); |
| |
| |
| int num_operands = 0; |
| BrigRegisterKind reg_kind = (BrigRegisterKind)0; |
| for (int offset = 0; offset < data_p->byteCount; offset += 4) { |
| const BrigOperand *op_p = (const BrigOperand *) |
| obj->getOperand(((int *)data_p->bytes)[offset/4]); |
| |
| if (op_p->kind == BRIG_KIND_OPERAND_REGISTER) { |
| const BrigOperandRegister *brigRegOp = |
| (const BrigOperandRegister*)op_p; |
| reg_kind = (BrigRegisterKind)brigRegOp->regKind; |
| } else if (op_p->kind == BRIG_KIND_OPERAND_CONSTANT_BYTES) { |
| uint16_t num_bytes = |
| ((Brig::BrigOperandConstantBytes*)op_p)->base.byteCount |
| - sizeof(BrigBase); |
| if (num_bytes == sizeof(uint32_t)) { |
| reg_kind = BRIG_REGISTER_KIND_SINGLE; |
| } else if (num_bytes == sizeof(uint64_t)) { |
| reg_kind = BRIG_REGISTER_KIND_DOUBLE; |
| } else { |
| fatal("OperandList: bad operand size %d\n", num_bytes); |
| } |
| } else if (op_p->kind == BRIG_KIND_OPERAND_WAVESIZE) { |
| reg_kind = BRIG_REGISTER_KIND_DOUBLE; |
| } else { |
| fatal("OperandList: bad operand kind %d\n", op_p->kind); |
| } |
| |
| num_operands++; |
| } |
| assert(baseOp->kind == BRIG_KIND_OPERAND_OPERAND_LIST); |
| |
| return BrigRegOperandInfo((BrigKind16_t)baseOp->kind, reg_kind); |
| } |
| break; |
| |
| case BRIG_KIND_OPERAND_ADDRESS: |
| { |
| const BrigOperandAddress *op = (BrigOperandAddress*)baseOp; |
| |
| if (!op->reg) { |
| BrigType type = BRIG_TYPE_NONE; |
| |
| if (op->symbol) { |
| const BrigDirective *dir = (BrigDirective*) |
| obj->getCodeSectionEntry(op->symbol); |
| |
| assert(dir->kind == BRIG_KIND_DIRECTIVE_VARIABLE); |
| |
| const BrigDirectiveVariable *sym = |
| (const BrigDirectiveVariable*)dir; |
| |
| type = (BrigType)sym->type; |
| } |
| return BrigRegOperandInfo(BRIG_KIND_OPERAND_ADDRESS, |
| (BrigType)type); |
| } else { |
| const BrigOperandAddress *b = (const BrigOperandAddress*)baseOp; |
| const BrigOperand *reg = obj->getOperand(b->reg); |
| const BrigOperandRegister *rop = (BrigOperandRegister*)reg; |
| |
| return BrigRegOperandInfo(BRIG_KIND_OPERAND_REGISTER, |
| (BrigRegisterKind)rop->regKind); |
| } |
| } |
| break; |
| |
| default: |
| fatal("AddrOperand: bad operand kind %d\n", baseOp->kind); |
| break; |
| } |
| } |
| |
| void |
| AddrOperandBase::parseAddr(const BrigOperandAddress *op, const BrigObject *obj) |
| { |
| assert(op->base.kind == BRIG_KIND_OPERAND_ADDRESS); |
| |
| const BrigDirective *d = |
| (BrigDirective*)obj->getCodeSectionEntry(op->symbol); |
| |
| /** |
| * HSAIL does not properly handle immediate offsets for instruction types |
| * that utilize them. It currently only supports instructions that use |
| * variables instead. Again, these pop up in code that is never executed |
| * (i.e. the HCC AMP codes) so we just hack it here to let us pass through |
| * the HSAIL object initialization. If such code is ever called, we would |
| * have to implement this properly. |
| */ |
| if (d->kind != BRIG_KIND_DIRECTIVE_VARIABLE) { |
| warn("HSAIL implementation does not support instructions with " |
| "address calculations where the operand is not a variable\n"); |
| } |
| |
| const BrigDirectiveVariable *sym = (BrigDirectiveVariable*)d; |
| name = obj->getString(sym->name); |
| |
| if (sym->segment != BRIG_SEGMENT_ARG) { |
| storageElement = |
| obj->currentCode->storageMap->findSymbol(sym->segment, name); |
| offset = 0; |
| } else { |
| // sym->name does not work for BRIG_SEGMENT_ARG for the following case: |
| // |
| // void foo(int a); |
| // void bar(double a); |
| // |
| // foo(...) --> arg_u32 %param_p0; |
| // st_arg_u32 $s0, [%param_p0]; |
| // call &foo (%param_p0); |
| // bar(...) --> arg_f64 %param_p0; |
| // st_arg_u64 $d0, [%param_p0]; |
| // call &foo (%param_p0); |
| // |
| // Both functions use the same variable name (param_p0)!!! |
| // |
| // Maybe this is a bug in the compiler (I don't know). |
| // |
| // Solution: |
| // Use directive pointer (BrigDirectiveVariable) to differentiate 2 |
| // versions of param_p0. |
| // |
| // Note this solution is kind of stupid, because we are pulling stuff |
| // out of the brig binary via the directive pointer and putting it into |
| // the symbol table, but now we are indexing the symbol table by the |
| // brig directive pointer! It makes the symbol table sort of pointless. |
| // But I don't want to mess with the rest of the infrastructure, so |
| // let's go with this for now. |
| // |
| // When we update the compiler again, we should see if this problem goes |
| // away. If so, we can fold some of this functionality into the code for |
| // kernel arguments. If not, maybe we can index the symbol name on a |
| // hash of the variable AND function name |
| storageElement = obj->currentCode-> |
| storageMap->findSymbol((Brig::BrigSegment)sym->segment, sym); |
| |
| assert(storageElement); |
| } |
| } |
| |
| uint64_t |
| AddrOperandBase::calcUniformBase() |
| { |
| // start with offset, will be 0 if not specified |
| uint64_t address = offset; |
| |
| // add in symbol value if specified |
| if (storageElement) { |
| address += storageElement->offset; |
| } |
| |
| return address; |
| } |
| |
| std::string |
| AddrOperandBase::disassemble(std::string reg_disassembly) |
| { |
| std::string disasm; |
| |
| if (offset || reg_disassembly != "") { |
| disasm += "["; |
| |
| if (reg_disassembly != "") { |
| disasm += reg_disassembly; |
| |
| if (offset > 0) { |
| disasm += "+"; |
| } |
| } |
| |
| if (offset) { |
| disasm += csprintf("%d", offset); |
| } |
| |
| disasm += "]"; |
| } else if (name) { |
| disasm += csprintf("[%s]", name); |
| } |
| |
| return disasm; |
| } |
| |
| void |
| NoRegAddrOperand::init(unsigned opOffset, const BrigObject *obj) |
| { |
| const BrigOperand *baseOp = obj->getOperand(opOffset); |
| |
| if (baseOp->kind == BRIG_KIND_OPERAND_ADDRESS) { |
| BrigOperandAddress *addrOp = (BrigOperandAddress*)baseOp; |
| parseAddr(addrOp, obj); |
| offset = (uint64_t(addrOp->offset.hi) << 32) | |
| uint64_t(addrOp->offset.lo); |
| } else { |
| fatal("NoRegAddrOperand: bad operand kind %d\n", baseOp->kind); |
| } |
| |
| } |
| |
| std::string |
| NoRegAddrOperand::disassemble() |
| { |
| return AddrOperandBase::disassemble(std::string("")); |
| } |
| |
| void |
| LabelOperand::init(unsigned opOffset, const BrigObject *obj) |
| { |
| const BrigOperandCodeRef *op = |
| (const BrigOperandCodeRef*)obj->getOperand(opOffset); |
| |
| assert(op->base.kind == BRIG_KIND_OPERAND_CODE_REF); |
| |
| const BrigDirective *dir = |
| (const BrigDirective*)obj->getCodeSectionEntry(op->ref); |
| |
| assert(dir->kind == BRIG_KIND_DIRECTIVE_LABEL); |
| label = obj->currentCode->refLabel((BrigDirectiveLabel*)dir, obj); |
| } |
| |
| uint32_t |
| LabelOperand::getTarget(Wavefront *w, int lane) |
| { |
| return label->get(); |
| } |
| |
| std::string |
| LabelOperand::disassemble() |
| { |
| return label->name; |
| } |