1/* 2 * Copyright (c) 2012-2015 Advanced Micro Devices, Inc. 3 * All rights reserved. 4 * 5 * For use for simulation and test purposes only 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright notice, 11 * this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright notice, 14 * this list of conditions and the following disclaimer in the documentation 15 * and/or other materials provided with the distribution. 16 * 17 * 3. Neither the name of the copyright holder nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 * POSSIBILITY OF SUCH DAMAGE. 32 * 33 * Author: Steve Reinhardt 34 */ 35 36#include "arch/hsail/operand.hh" 37 38using namespace Brig; 39 40bool 41BaseRegOperand::init(unsigned opOffset, const BrigObject *obj, 42 unsigned &maxRegIdx, char _regFileChar) 43{ 44 regFileChar = _regFileChar; 45 const BrigOperand *brigOp = obj->getOperand(opOffset); 46 47 if (brigOp->kind != BRIG_KIND_OPERAND_REGISTER) 48 return false; 49 50 const BrigOperandRegister *brigRegOp = (const BrigOperandRegister*)brigOp; 51 52 regIdx = brigRegOp->regNum; 53 54 DPRINTF(GPUReg, "Operand: regNum: %d, kind: %d\n", regIdx, 55 brigRegOp->regKind); 56 57 maxRegIdx = std::max(maxRegIdx, regIdx); 58 59 return true; 60} 61 62void 63ListOperand::init(unsigned opOffset, const BrigObject *obj) 64{ 65 const BrigOperand *brigOp = (const BrigOperand*)obj->getOperand(opOffset); 66 67 switch (brigOp->kind) { 68 case BRIG_KIND_OPERAND_CODE_LIST: 69 { 70 const BrigOperandCodeList *opList = 71 (const BrigOperandCodeList*)brigOp; 72 73 const Brig::BrigData *oprnd_data = 74 obj->getBrigBaseData(opList->elements); 75 76 // Note: for calls Dest list of operands could be size of 0. 77 elementCount = oprnd_data->byteCount / 4; 78 79 DPRINTF(GPUReg, "Operand Code List: # elements: %d\n", 80 elementCount); 81 82 for (int i = 0; i < elementCount; ++i) { 83 unsigned *data_offset = 84 (unsigned*)obj->getData(opList->elements + 4 * (i + 1)); 85 86 const BrigDirectiveVariable *p = 87 (const BrigDirectiveVariable*)obj-> 88 getCodeSectionEntry(*data_offset); 89 90 StorageElement *se = obj->currentCode->storageMap-> 91 findSymbol(BRIG_SEGMENT_ARG, p); 92 93 assert(se); 94 callArgs.push_back(se); 95 } 96 } 97 break; 98 default: 99 fatal("ListOperand: bad operand kind %d\n", brigOp->kind); 100 } 101} 102 103std::string 104ListOperand::disassemble() 105{ 106 std::string res_str(""); 107 108 for (auto it : callArgs) { 109 res_str += csprintf("%s ", it->name.c_str()); 110 } 111 112 return res_str; 113} 114 115void 116FunctionRefOperand::init(unsigned opOffset, const BrigObject *obj) 117{ 118 const BrigOperand *baseOp = obj->getOperand(opOffset); 119 120 if (baseOp->kind != BRIG_KIND_OPERAND_CODE_REF) { 121 fatal("FunctionRefOperand: bad operand kind %d\n", baseOp->kind); 122 } 123 124 const BrigOperandCodeRef *brigOp = (const BrigOperandCodeRef*)baseOp; 125 126 const BrigDirectiveExecutable *p = 127 (const BrigDirectiveExecutable*)obj->getCodeSectionEntry(brigOp->ref); 128 129 func_name = obj->getString(p->name); 130} 131 132std::string 133FunctionRefOperand::disassemble() 134{ 135 DPRINTF(GPUReg, "Operand Func-ref name: %s\n", func_name); 136 137 return csprintf("%s", func_name); 138} 139 140bool 141BaseRegOperand::init_from_vect(unsigned opOffset, const BrigObject *obj, 142 int at, unsigned &maxRegIdx, char _regFileChar) 143{ 144 regFileChar = _regFileChar; 145 const BrigOperand *brigOp = obj->getOperand(opOffset); 146 147 if (brigOp->kind != BRIG_KIND_OPERAND_OPERAND_LIST) 148 return false; 149 150 151 const Brig::BrigOperandOperandList *brigRegVecOp = 152 (const Brig::BrigOperandOperandList*)brigOp; 153 154 unsigned *data_offset = 155 (unsigned*)obj->getData(brigRegVecOp->elements + 4 * (at + 1)); 156 157 const BrigOperand *p = 158 (const BrigOperand*)obj->getOperand(*data_offset); 159 if (p->kind != BRIG_KIND_OPERAND_REGISTER) { 160 return false; 161 } 162 163 const BrigOperandRegister *brigRegOp =(const BrigOperandRegister*)p; 164 165 regIdx = brigRegOp->regNum; 166 167 DPRINTF(GPUReg, "Operand: regNum: %d, kind: %d \n", regIdx, 168 brigRegOp->regKind); 169 170 maxRegIdx = std::max(maxRegIdx, regIdx); 171 172 return true; 173} 174 175void 176BaseRegOperand::initWithStrOffset(unsigned strOffset, const BrigObject *obj, 177 unsigned &maxRegIdx, char _regFileChar) 178{ 179 const char *name = obj->getString(strOffset); 180 char *endptr; 181 regIdx = strtoul(name + 2, &endptr, 10); 182 183 if (name[0] != '$' || name[1] != _regFileChar) { 184 fatal("register operand parse error on \"%s\"\n", name); 185 } 186 187 maxRegIdx = std::max(maxRegIdx, regIdx); 188} 189 190unsigned SRegOperand::maxRegIdx; 191unsigned DRegOperand::maxRegIdx; 192unsigned CRegOperand::maxRegIdx; 193 194std::string 195SRegOperand::disassemble() 196{ 197 return csprintf("$s%d", regIdx); 198} 199 200std::string 201DRegOperand::disassemble() 202{ 203 return csprintf("$d%d", regIdx); 204} 205 206std::string 207CRegOperand::disassemble() 208{ 209 return csprintf("$c%d", regIdx); 210} 211 212BrigRegOperandInfo 213findRegDataType(unsigned opOffset, const BrigObject *obj) 214{ 215 const BrigOperand *baseOp = obj->getOperand(opOffset); 216 217 switch (baseOp->kind) { 218 case BRIG_KIND_OPERAND_REGISTER: 219 { 220 const BrigOperandRegister *op = (BrigOperandRegister*)baseOp; 221 222 return BrigRegOperandInfo((BrigKind16_t)baseOp->kind, 223 (BrigRegisterKind)op->regKind); 224 } 225 break; 226 227 case BRIG_KIND_OPERAND_WAVESIZE: 228 { 229 BrigRegisterKind reg_kind = BRIG_REGISTER_KIND_DOUBLE; 230 return BrigRegOperandInfo((BrigKind16_t)baseOp->kind, reg_kind); 231 } 232 233 case BRIG_KIND_OPERAND_OPERAND_LIST: 234 { 235 const BrigOperandOperandList *op = 236 (BrigOperandOperandList*)baseOp; 237 const BrigData *data_p = (BrigData*)obj->getData(op->elements); 238 239 240 int num_operands = 0; 241 BrigRegisterKind reg_kind = (BrigRegisterKind)0; 242 for (int offset = 0; offset < data_p->byteCount; offset += 4) { 243 const BrigOperand *op_p = (const BrigOperand *) 244 obj->getOperand(((int *)data_p->bytes)[offset/4]); 245 246 if (op_p->kind == BRIG_KIND_OPERAND_REGISTER) { 247 const BrigOperandRegister *brigRegOp = 248 (const BrigOperandRegister*)op_p; 249 reg_kind = (BrigRegisterKind)brigRegOp->regKind; 250 } else if (op_p->kind == BRIG_KIND_OPERAND_CONSTANT_BYTES) { 251 uint16_t num_bytes = 252 ((Brig::BrigOperandConstantBytes*)op_p)->base.byteCount 253 - sizeof(BrigBase); 254 if (num_bytes == sizeof(uint32_t)) { 255 reg_kind = BRIG_REGISTER_KIND_SINGLE; 256 } else if (num_bytes == sizeof(uint64_t)) { 257 reg_kind = BRIG_REGISTER_KIND_DOUBLE; 258 } else { 259 fatal("OperandList: bad operand size %d\n", num_bytes); 260 } 261 } else if (op_p->kind == BRIG_KIND_OPERAND_WAVESIZE) { 262 reg_kind = BRIG_REGISTER_KIND_DOUBLE; 263 } else { 264 fatal("OperandList: bad operand kind %d\n", op_p->kind); 265 } 266 267 num_operands++; 268 } 269 assert(baseOp->kind == BRIG_KIND_OPERAND_OPERAND_LIST); 270 271 return BrigRegOperandInfo((BrigKind16_t)baseOp->kind, reg_kind); 272 } 273 break; 274 275 case BRIG_KIND_OPERAND_ADDRESS: 276 { 277 const BrigOperandAddress *op = (BrigOperandAddress*)baseOp; 278 279 if (!op->reg) { 280 BrigType type = BRIG_TYPE_NONE; 281 282 if (op->symbol) { 283 const BrigDirective *dir = (BrigDirective*) 284 obj->getCodeSectionEntry(op->symbol); 285 286 assert(dir->kind == BRIG_KIND_DIRECTIVE_VARIABLE); 287 288 const BrigDirectiveVariable *sym = 289 (const BrigDirectiveVariable*)dir; 290 291 type = (BrigType)sym->type; 292 } 293 return BrigRegOperandInfo(BRIG_KIND_OPERAND_ADDRESS, 294 (BrigType)type); 295 } else { 296 const BrigOperandAddress *b = (const BrigOperandAddress*)baseOp; 297 const BrigOperand *reg = obj->getOperand(b->reg); 298 const BrigOperandRegister *rop = (BrigOperandRegister*)reg; 299 300 return BrigRegOperandInfo(BRIG_KIND_OPERAND_REGISTER, 301 (BrigRegisterKind)rop->regKind); 302 } 303 } 304 break; 305 306 default: 307 fatal("AddrOperand: bad operand kind %d\n", baseOp->kind); 308 break; 309 } 310} 311 312void 313AddrOperandBase::parseAddr(const BrigOperandAddress *op, const BrigObject *obj) 314{ 315 assert(op->base.kind == BRIG_KIND_OPERAND_ADDRESS); 316 317 const BrigDirective *d = 318 (BrigDirective*)obj->getCodeSectionEntry(op->symbol); 319 320 /** 321 * HSAIL does not properly handle immediate offsets for instruction types 322 * that utilize them. It currently only supports instructions that use 323 * variables instead. Again, these pop up in code that is never executed 324 * (i.e. the HCC AMP codes) so we just hack it here to let us pass through 325 * the HSAIL object initialization. If such code is ever called, we would 326 * have to implement this properly. 327 */ 328 if (d->kind != BRIG_KIND_DIRECTIVE_VARIABLE) { 329 warn("HSAIL implementation does not support instructions with " 330 "address calculations where the operand is not a variable\n"); 331 } 332 333 const BrigDirectiveVariable *sym = (BrigDirectiveVariable*)d; 334 name = obj->getString(sym->name); 335 336 if (sym->segment != BRIG_SEGMENT_ARG) { 337 storageElement = 338 obj->currentCode->storageMap->findSymbol(sym->segment, name); 339 offset = 0; 340 } else { 341 // sym->name does not work for BRIG_SEGMENT_ARG for the following case: 342 // 343 // void foo(int a); 344 // void bar(double a); 345 // 346 // foo(...) --> arg_u32 %param_p0; 347 // st_arg_u32 $s0, [%param_p0]; 348 // call &foo (%param_p0); 349 // bar(...) --> arg_f64 %param_p0; 350 // st_arg_u64 $d0, [%param_p0]; 351 // call &foo (%param_p0); 352 // 353 // Both functions use the same variable name (param_p0)!!! 354 // 355 // Maybe this is a bug in the compiler (I don't know). 356 // 357 // Solution: 358 // Use directive pointer (BrigDirectiveVariable) to differentiate 2 359 // versions of param_p0. 360 // 361 // Note this solution is kind of stupid, because we are pulling stuff 362 // out of the brig binary via the directive pointer and putting it into 363 // the symbol table, but now we are indexing the symbol table by the 364 // brig directive pointer! It makes the symbol table sort of pointless. 365 // But I don't want to mess with the rest of the infrastructure, so 366 // let's go with this for now. 367 // 368 // When we update the compiler again, we should see if this problem goes 369 // away. If so, we can fold some of this functionality into the code for 370 // kernel arguments. If not, maybe we can index the symbol name on a 371 // hash of the variable AND function name 372 storageElement = obj->currentCode-> 373 storageMap->findSymbol((Brig::BrigSegment)sym->segment, sym); 374 375 assert(storageElement); 376 } 377} 378 379uint64_t 380AddrOperandBase::calcUniformBase() 381{ 382 // start with offset, will be 0 if not specified 383 uint64_t address = offset; 384 385 // add in symbol value if specified 386 if (storageElement) { 387 address += storageElement->offset; 388 } 389 390 return address; 391} 392 393std::string 394AddrOperandBase::disassemble(std::string reg_disassembly) 395{ 396 std::string disasm; 397 398 if (offset || reg_disassembly != "") { 399 disasm += "["; 400 401 if (reg_disassembly != "") { 402 disasm += reg_disassembly; 403 404 if (offset > 0) { 405 disasm += "+"; 406 } 407 } 408 409 if (offset) { 410 disasm += csprintf("%d", offset); 411 } 412 413 disasm += "]"; 414 } else if (name) { 415 disasm += csprintf("[%s]", name); 416 } 417 418 return disasm; 419} 420 421void 422NoRegAddrOperand::init(unsigned opOffset, const BrigObject *obj) 423{ 424 const BrigOperand *baseOp = obj->getOperand(opOffset); 425 426 if (baseOp->kind == BRIG_KIND_OPERAND_ADDRESS) { 427 BrigOperandAddress *addrOp = (BrigOperandAddress*)baseOp; 428 parseAddr(addrOp, obj); 429 offset = (uint64_t(addrOp->offset.hi) << 32) | 430 uint64_t(addrOp->offset.lo); 431 } else { 432 fatal("NoRegAddrOperand: bad operand kind %d\n", baseOp->kind); 433 } 434 435} 436 437std::string 438NoRegAddrOperand::disassemble() 439{ 440 return AddrOperandBase::disassemble(std::string("")); 441} 442 443void 444LabelOperand::init(unsigned opOffset, const BrigObject *obj) 445{ 446 const BrigOperandCodeRef *op = 447 (const BrigOperandCodeRef*)obj->getOperand(opOffset); 448 449 assert(op->base.kind == BRIG_KIND_OPERAND_CODE_REF); 450 451 const BrigDirective *dir = 452 (const BrigDirective*)obj->getCodeSectionEntry(op->ref); 453 454 assert(dir->kind == BRIG_KIND_DIRECTIVE_LABEL); 455 label = obj->currentCode->refLabel((BrigDirectiveLabel*)dir, obj); 456} 457 458uint32_t 459LabelOperand::getTarget(Wavefront *w, int lane) 460{ 461 return label->get(); 462} 463 464std::string 465LabelOperand::disassemble() 466{ 467 return label->name; 468} 469