| /* | |
| * 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; | |
| } |