blob: 993d3521fb4c653a319f966650b96899f368a559 [file] [log] [blame]
/*
* 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;
}