487 строки
15 KiB
C++
487 строки
15 KiB
C++
///////////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// HLOperations.cpp //
|
|
// Copyright (C) Microsoft Corporation. All rights reserved. //
|
|
// This file is distributed under the University of Illinois Open Source //
|
|
// License. See LICENSE.TXT for details. //
|
|
// //
|
|
// Implementation of DXIL operations. //
|
|
// //
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#pragma once
|
|
|
|
#include "dxc/HLSL/HLOperations.h"
|
|
#include "dxc/HlslIntrinsicOp.h"
|
|
#include "llvm/IR/Function.h"
|
|
#include "llvm/IR/Instructions.h"
|
|
#include "llvm/IR/Module.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
using namespace hlsl;
|
|
using namespace llvm;
|
|
|
|
namespace hlsl {
|
|
|
|
const char HLPrefixStr [] = "dx.hl";
|
|
const char * const HLPrefix = HLPrefixStr;
|
|
static const char HLLowerStrategyStr[] = "dx.hlls";
|
|
static const char * const HLLowerStrategy = HLLowerStrategyStr;
|
|
|
|
static StringRef HLOpcodeGroupNames[]{
|
|
"notHLDXIL", // NotHL,
|
|
"<ext>", // HLExtIntrinsic - should always refer through extension
|
|
"op", // HLIntrinsic,
|
|
"cast", // HLCast,
|
|
"init", // HLInit,
|
|
"binop", // HLBinOp,
|
|
"unop", // HLUnOp,
|
|
"subscript", // HLSubscript,
|
|
"matldst", // HLMatLoadStore,
|
|
"select", // HLSelect,
|
|
"createhandle",// HLCreateHandle,
|
|
"numOfHLDXIL", // NumOfHLOps
|
|
};
|
|
|
|
static StringRef HLOpcodeGroupFullNames[]{
|
|
"notHLDXIL", // NotHL,
|
|
"<ext>", // HLExtIntrinsic - should aways refer through extension
|
|
"dx.hl.op", // HLIntrinsic,
|
|
"dx.hl.cast", // HLCast,
|
|
"dx.hl.init", // HLInit,
|
|
"dx.hl.binop", // HLBinOp,
|
|
"dx.hl.unop", // HLUnOp,
|
|
"dx.hl.subscript", // HLSubscript,
|
|
"dx.hl.matldst", // HLMatLoadStore,
|
|
"dx.hl.select", // HLSelect,
|
|
"dx.hl.createhandle", // HLCreateHandle,
|
|
"numOfHLDXIL", // NumOfHLOps
|
|
};
|
|
|
|
static HLOpcodeGroup GetHLOpcodeGroupInternal(StringRef group) {
|
|
if (!group.empty()) {
|
|
switch (group[0]) {
|
|
case 'o': // op
|
|
return HLOpcodeGroup::HLIntrinsic;
|
|
case 'c': // cast
|
|
switch (group[1]) {
|
|
case 'a': // cast
|
|
return HLOpcodeGroup::HLCast;
|
|
case 'r': // createhandle
|
|
return HLOpcodeGroup::HLCreateHandle;
|
|
}
|
|
case 'i': // init
|
|
return HLOpcodeGroup::HLInit;
|
|
case 'b': // binaryOp
|
|
return HLOpcodeGroup::HLBinOp;
|
|
case 'u': // unaryOp
|
|
return HLOpcodeGroup::HLUnOp;
|
|
case 's': // subscript
|
|
switch (group[1]) {
|
|
case 'u':
|
|
return HLOpcodeGroup::HLSubscript;
|
|
case 'e':
|
|
return HLOpcodeGroup::HLSelect;
|
|
}
|
|
case 'm': // matldst
|
|
return HLOpcodeGroup::HLMatLoadStore;
|
|
}
|
|
}
|
|
return HLOpcodeGroup::NotHL;
|
|
}
|
|
// GetHLOpGroup by function name.
|
|
HLOpcodeGroup GetHLOpcodeGroupByName(const Function *F) {
|
|
StringRef name = F->getName();
|
|
|
|
if (!name.startswith(HLPrefix)) {
|
|
// This could be an external intrinsic, but this function
|
|
// won't recognize those as such. Use GetHLOpcodeGroupByName
|
|
// to make that distinction.
|
|
return HLOpcodeGroup::NotHL;
|
|
}
|
|
|
|
const unsigned prefixSize = sizeof(HLPrefixStr);
|
|
|
|
StringRef group = name.substr(prefixSize);
|
|
return GetHLOpcodeGroupInternal(group);
|
|
}
|
|
|
|
HLOpcodeGroup GetHLOpcodeGroup(llvm::Function *F) {
|
|
llvm::StringRef name = GetHLOpcodeGroupNameByAttr(F);
|
|
HLOpcodeGroup result = GetHLOpcodeGroupInternal(name);
|
|
if (result == HLOpcodeGroup::NotHL) {
|
|
result = name.empty() ? result : HLOpcodeGroup::HLExtIntrinsic;
|
|
}
|
|
if (result == HLOpcodeGroup::NotHL) {
|
|
result = GetHLOpcodeGroupByName(F);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
llvm::StringRef GetHLOpcodeGroupNameByAttr(llvm::Function *F) {
|
|
Attribute groupAttr = F->getFnAttribute(hlsl::HLPrefix);
|
|
StringRef group = groupAttr.getValueAsString();
|
|
return group;
|
|
}
|
|
|
|
StringRef GetHLOpcodeGroupName(HLOpcodeGroup op) {
|
|
switch (op) {
|
|
case HLOpcodeGroup::HLCast:
|
|
case HLOpcodeGroup::HLInit:
|
|
case HLOpcodeGroup::HLBinOp:
|
|
case HLOpcodeGroup::HLUnOp:
|
|
case HLOpcodeGroup::HLIntrinsic:
|
|
case HLOpcodeGroup::HLSubscript:
|
|
case HLOpcodeGroup::HLMatLoadStore:
|
|
case HLOpcodeGroup::HLSelect:
|
|
case HLOpcodeGroup::HLCreateHandle:
|
|
return HLOpcodeGroupNames[static_cast<unsigned>(op)];
|
|
default:
|
|
llvm_unreachable("invalid op");
|
|
|
|
return "";
|
|
}
|
|
}
|
|
StringRef GetHLOpcodeGroupFullName(HLOpcodeGroup op) {
|
|
switch (op) {
|
|
case HLOpcodeGroup::HLCast:
|
|
case HLOpcodeGroup::HLInit:
|
|
case HLOpcodeGroup::HLBinOp:
|
|
case HLOpcodeGroup::HLUnOp:
|
|
case HLOpcodeGroup::HLIntrinsic:
|
|
case HLOpcodeGroup::HLSubscript:
|
|
case HLOpcodeGroup::HLMatLoadStore:
|
|
case HLOpcodeGroup::HLSelect:
|
|
case HLOpcodeGroup::HLCreateHandle:
|
|
return HLOpcodeGroupFullNames[static_cast<unsigned>(op)];
|
|
default:
|
|
llvm_unreachable("invalid op");
|
|
return "";
|
|
}
|
|
}
|
|
|
|
llvm::StringRef GetHLOpcodeName(HLUnaryOpcode Op) {
|
|
switch (Op) {
|
|
case HLUnaryOpcode::PostInc: return "++";
|
|
case HLUnaryOpcode::PostDec: return "--";
|
|
case HLUnaryOpcode::PreInc: return "++";
|
|
case HLUnaryOpcode::PreDec: return "--";
|
|
case HLUnaryOpcode::Plus: return "+";
|
|
case HLUnaryOpcode::Minus: return "-";
|
|
case HLUnaryOpcode::Not: return "~";
|
|
case HLUnaryOpcode::LNot: return "!";
|
|
}
|
|
llvm_unreachable("Unknown unary operator");
|
|
|
|
}
|
|
|
|
llvm::StringRef GetHLOpcodeName(HLBinaryOpcode Op) {
|
|
switch (Op) {
|
|
case HLBinaryOpcode::Mul: return "*";
|
|
case HLBinaryOpcode::UDiv:
|
|
case HLBinaryOpcode::Div: return "/";
|
|
case HLBinaryOpcode::URem:
|
|
case HLBinaryOpcode::Rem: return "%";
|
|
case HLBinaryOpcode::Add: return "+";
|
|
case HLBinaryOpcode::Sub: return "-";
|
|
case HLBinaryOpcode::Shl: return "<<";
|
|
case HLBinaryOpcode::UShr:
|
|
case HLBinaryOpcode::Shr: return ">>";
|
|
case HLBinaryOpcode::ULT:
|
|
case HLBinaryOpcode::LT: return "<";
|
|
case HLBinaryOpcode::UGT:
|
|
case HLBinaryOpcode::GT: return ">";
|
|
case HLBinaryOpcode::ULE:
|
|
case HLBinaryOpcode::LE: return "<=";
|
|
case HLBinaryOpcode::UGE:
|
|
case HLBinaryOpcode::GE: return ">=";
|
|
case HLBinaryOpcode::EQ: return "==";
|
|
case HLBinaryOpcode::NE: return "!=";
|
|
case HLBinaryOpcode::And: return "&";
|
|
case HLBinaryOpcode::Xor: return "^";
|
|
case HLBinaryOpcode::Or: return "|";
|
|
case HLBinaryOpcode::LAnd: return "&&";
|
|
case HLBinaryOpcode::LOr: return "||";
|
|
}
|
|
|
|
llvm_unreachable("Invalid OpCode!");
|
|
}
|
|
|
|
llvm::StringRef GetHLOpcodeName(HLSubscriptOpcode Op) {
|
|
switch (Op) {
|
|
case HLSubscriptOpcode::DefaultSubscript:
|
|
return "[]";
|
|
case HLSubscriptOpcode::ColMatSubscript:
|
|
return "colMajor[]";
|
|
case HLSubscriptOpcode::RowMatSubscript:
|
|
return "rowMajor[]";
|
|
case HLSubscriptOpcode::ColMatElement:
|
|
return "colMajor_m";
|
|
case HLSubscriptOpcode::RowMatElement:
|
|
return "rowMajor_m";
|
|
case HLSubscriptOpcode::DoubleSubscript:
|
|
return "[][]";
|
|
case HLSubscriptOpcode::CBufferSubscript:
|
|
return "cb";
|
|
case HLSubscriptOpcode::VectorSubscript:
|
|
return "vector[]";
|
|
}
|
|
return "";
|
|
}
|
|
|
|
llvm::StringRef GetHLOpcodeName(HLCastOpcode Op) {
|
|
switch (Op) {
|
|
case HLCastOpcode::DefaultCast:
|
|
return "default";
|
|
case HLCastOpcode::ToUnsignedCast:
|
|
return "toUnsigned";
|
|
case HLCastOpcode::FromUnsignedCast:
|
|
return "fromUnsigned";
|
|
case HLCastOpcode::UnsignedUnsignedCast:
|
|
return "unsignedUnsigned";
|
|
case HLCastOpcode::ColMatrixToVecCast:
|
|
return "colMatToVec";
|
|
case HLCastOpcode::RowMatrixToVecCast:
|
|
return "rowMatToVec";
|
|
case HLCastOpcode::ColMatrixToRowMatrix:
|
|
return "colMatToRowMat";
|
|
case HLCastOpcode::RowMatrixToColMatrix:
|
|
return "rowMatToColMat";
|
|
case HLCastOpcode::HandleToResCast:
|
|
return "handleToRes";
|
|
}
|
|
return "";
|
|
}
|
|
|
|
llvm::StringRef GetHLOpcodeName(HLMatLoadStoreOpcode Op) {
|
|
switch (Op) {
|
|
case HLMatLoadStoreOpcode::ColMatLoad:
|
|
return "colLoad";
|
|
case HLMatLoadStoreOpcode::ColMatStore:
|
|
return "colStore";
|
|
case HLMatLoadStoreOpcode::RowMatLoad:
|
|
return "rowLoad";
|
|
case HLMatLoadStoreOpcode::RowMatStore:
|
|
return "rowStore";
|
|
}
|
|
llvm_unreachable("invalid matrix load store operator");
|
|
}
|
|
|
|
StringRef GetHLLowerStrategy(Function *F) {
|
|
llvm::Attribute A = F->getFnAttribute(HLLowerStrategy);
|
|
llvm::StringRef LowerStrategy = A.getValueAsString();
|
|
return LowerStrategy;
|
|
}
|
|
|
|
void SetHLLowerStrategy(Function *F, StringRef S) {
|
|
F->addFnAttr(HLLowerStrategy, S);
|
|
}
|
|
|
|
std::string GetHLFullName(HLOpcodeGroup op, unsigned opcode) {
|
|
assert(op != HLOpcodeGroup::HLExtIntrinsic && "else table name should be used");
|
|
std::string opName = GetHLOpcodeGroupFullName(op).str() + ".";
|
|
|
|
switch (op) {
|
|
case HLOpcodeGroup::HLBinOp: {
|
|
HLBinaryOpcode binOp = static_cast<HLBinaryOpcode>(opcode);
|
|
return opName + GetHLOpcodeName(binOp).str();
|
|
}
|
|
case HLOpcodeGroup::HLUnOp: {
|
|
HLUnaryOpcode unOp = static_cast<HLUnaryOpcode>(opcode);
|
|
return opName + GetHLOpcodeName(unOp).str();
|
|
}
|
|
case HLOpcodeGroup::HLIntrinsic: {
|
|
// intrinsic with same signature will share the funciton now
|
|
// The opcode is in arg0.
|
|
return opName;
|
|
}
|
|
case HLOpcodeGroup::HLMatLoadStore: {
|
|
HLMatLoadStoreOpcode matOp = static_cast<HLMatLoadStoreOpcode>(opcode);
|
|
return opName + GetHLOpcodeName(matOp).str();
|
|
}
|
|
case HLOpcodeGroup::HLSubscript: {
|
|
HLSubscriptOpcode subOp = static_cast<HLSubscriptOpcode>(opcode);
|
|
return opName + GetHLOpcodeName(subOp).str();
|
|
}
|
|
case HLOpcodeGroup::HLCast: {
|
|
HLCastOpcode castOp = static_cast<HLCastOpcode>(opcode);
|
|
return opName + GetHLOpcodeName(castOp).str();
|
|
}
|
|
default:
|
|
return opName;
|
|
}
|
|
}
|
|
|
|
// Get opcode from arg0 of function call.
|
|
unsigned GetHLOpcode(const CallInst *CI) {
|
|
Value *idArg = CI->getArgOperand(HLOperandIndex::kOpcodeIdx);
|
|
Constant *idConst = cast<Constant>(idArg);
|
|
return idConst->getUniqueInteger().getLimitedValue();
|
|
}
|
|
|
|
unsigned GetRowMajorOpcode(HLOpcodeGroup group, unsigned opcode) {
|
|
switch (group) {
|
|
case HLOpcodeGroup::HLMatLoadStore: {
|
|
HLMatLoadStoreOpcode matOp = static_cast<HLMatLoadStoreOpcode>(opcode);
|
|
switch (matOp) {
|
|
case HLMatLoadStoreOpcode::ColMatLoad:
|
|
return static_cast<unsigned>(HLMatLoadStoreOpcode::RowMatLoad);
|
|
case HLMatLoadStoreOpcode::ColMatStore:
|
|
return static_cast<unsigned>(HLMatLoadStoreOpcode::RowMatStore);
|
|
default:
|
|
return opcode;
|
|
}
|
|
} break;
|
|
case HLOpcodeGroup::HLSubscript: {
|
|
HLSubscriptOpcode subOp = static_cast<HLSubscriptOpcode>(opcode);
|
|
switch (subOp) {
|
|
case HLSubscriptOpcode::ColMatElement:
|
|
return static_cast<unsigned>(HLSubscriptOpcode::RowMatElement);
|
|
case HLSubscriptOpcode::ColMatSubscript:
|
|
return static_cast<unsigned>(HLSubscriptOpcode::RowMatSubscript);
|
|
default:
|
|
return opcode;
|
|
}
|
|
} break;
|
|
default:
|
|
return opcode;
|
|
}
|
|
}
|
|
|
|
bool HasUnsignedOpcode(unsigned opcode) {
|
|
return HasUnsignedIntrinsicOpcode(static_cast<IntrinsicOp>(opcode));
|
|
}
|
|
|
|
unsigned GetUnsignedOpcode(unsigned opcode) {
|
|
return GetUnsignedIntrinsicOpcode(static_cast<IntrinsicOp>(opcode));
|
|
}
|
|
|
|
// For HLBinaryOpcode
|
|
bool HasUnsignedOpcode(HLBinaryOpcode opcode) {
|
|
switch (opcode) {
|
|
case HLBinaryOpcode::Div:
|
|
case HLBinaryOpcode::Rem:
|
|
case HLBinaryOpcode::Shr:
|
|
case HLBinaryOpcode::LT:
|
|
case HLBinaryOpcode::GT:
|
|
case HLBinaryOpcode::LE:
|
|
case HLBinaryOpcode::GE:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
HLBinaryOpcode GetUnsignedOpcode(HLBinaryOpcode opcode) {
|
|
switch (opcode) {
|
|
case HLBinaryOpcode::Div:
|
|
return HLBinaryOpcode::UDiv;
|
|
case HLBinaryOpcode::Rem:
|
|
return HLBinaryOpcode::URem;
|
|
case HLBinaryOpcode::Shr:
|
|
return HLBinaryOpcode::UShr;
|
|
case HLBinaryOpcode::LT:
|
|
return HLBinaryOpcode::ULT;
|
|
case HLBinaryOpcode::GT:
|
|
return HLBinaryOpcode::UGT;
|
|
case HLBinaryOpcode::LE:
|
|
return HLBinaryOpcode::ULE;
|
|
case HLBinaryOpcode::GE:
|
|
return HLBinaryOpcode::UGE;
|
|
default:
|
|
return opcode;
|
|
}
|
|
}
|
|
|
|
static void SetHLFunctionAttribute(Function *F, HLOpcodeGroup group,
|
|
unsigned opcode) {
|
|
switch (group) {
|
|
case HLOpcodeGroup::HLUnOp:
|
|
case HLOpcodeGroup::HLBinOp:
|
|
case HLOpcodeGroup::HLCast:
|
|
case HLOpcodeGroup::HLSubscript:
|
|
if (!F->hasFnAttribute(Attribute::ReadNone)) {
|
|
F->addFnAttr(Attribute::ReadNone);
|
|
F->addFnAttr(Attribute::NoUnwind);
|
|
}
|
|
break;
|
|
case HLOpcodeGroup::HLInit:
|
|
if (!F->hasFnAttribute(Attribute::ReadNone))
|
|
if (!F->getReturnType()->isVoidTy()) {
|
|
F->addFnAttr(Attribute::ReadNone);
|
|
F->addFnAttr(Attribute::NoUnwind);
|
|
}
|
|
break;
|
|
case HLOpcodeGroup::HLMatLoadStore: {
|
|
HLMatLoadStoreOpcode matOp = static_cast<HLMatLoadStoreOpcode>(opcode);
|
|
if (matOp == HLMatLoadStoreOpcode::ColMatLoad ||
|
|
matOp == HLMatLoadStoreOpcode::RowMatLoad)
|
|
if (!F->hasFnAttribute(Attribute::ReadOnly)) {
|
|
F->addFnAttr(Attribute::ReadOnly);
|
|
F->addFnAttr(Attribute::NoUnwind);
|
|
}
|
|
} break;
|
|
case HLOpcodeGroup::HLCreateHandle: {
|
|
F->addFnAttr(Attribute::ReadNone);
|
|
F->addFnAttr(Attribute::NoUnwind);
|
|
F->addFnAttr(Attribute::NoInline);
|
|
F->setLinkage(llvm::GlobalValue::LinkageTypes::InternalLinkage);
|
|
} break;
|
|
}
|
|
}
|
|
|
|
Function *GetOrCreateHLFunction(Module &M, FunctionType *funcTy,
|
|
HLOpcodeGroup group, unsigned opcode) {
|
|
return GetOrCreateHLFunction(M, funcTy, group, nullptr, nullptr, opcode);
|
|
}
|
|
|
|
Function *GetOrCreateHLFunction(Module &M, FunctionType *funcTy,
|
|
HLOpcodeGroup group, llvm::StringRef *groupName,
|
|
llvm::StringRef *fnName, unsigned opcode) {
|
|
std::string mangledName;
|
|
raw_string_ostream mangledNameStr(mangledName);
|
|
if (group == HLOpcodeGroup::HLExtIntrinsic) {
|
|
assert(groupName && "else intrinsic should have been rejected");
|
|
assert(fnName && "else intrinsic should have been rejected");
|
|
mangledNameStr << *groupName;
|
|
mangledNameStr << '.';
|
|
mangledNameStr << *fnName;
|
|
}
|
|
else {
|
|
mangledNameStr << GetHLFullName(group, opcode);
|
|
mangledNameStr << '.';
|
|
funcTy->print(mangledNameStr);
|
|
}
|
|
|
|
mangledNameStr.flush();
|
|
|
|
Function *F = cast<Function>(M.getOrInsertFunction(mangledName, funcTy));
|
|
if (group == HLOpcodeGroup::HLExtIntrinsic) {
|
|
F->addFnAttr(hlsl::HLPrefix, *groupName);
|
|
}
|
|
|
|
SetHLFunctionAttribute(F, group, opcode);
|
|
|
|
return F;
|
|
}
|
|
|
|
// HLFunction with body cannot share with HLFunction without body.
|
|
// So need add name.
|
|
Function *GetOrCreateHLFunctionWithBody(Module &M, FunctionType *funcTy,
|
|
HLOpcodeGroup group, unsigned opcode,
|
|
StringRef name) {
|
|
std::string operatorName = GetHLFullName(group, opcode);
|
|
std::string mangledName = operatorName + "." + name.str();
|
|
raw_string_ostream mangledNameStr(mangledName);
|
|
funcTy->print(mangledNameStr);
|
|
mangledNameStr.flush();
|
|
|
|
Function *F = cast<Function>(M.getOrInsertFunction(mangledName, funcTy));
|
|
|
|
SetHLFunctionAttribute(F, group, opcode);
|
|
|
|
return F;
|
|
}
|
|
|
|
} // namespace hlsl
|