469 lines
14 KiB
C++
469 lines
14 KiB
C++
/*
|
|
* 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;
|
|
}
|