blob: 905756469902569486c453eb26ed26b2120f084f [file] [log] [blame]
//===- DXILOpBuilder.cpp - Helper class for build DIXLOp functions --------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file This file contains class to help build DXIL op functions.
//===----------------------------------------------------------------------===//
#include "DXILOpBuilder.h"
#include "DXILConstants.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/DXILOperationCommon.h"
#include "llvm/Support/ErrorHandling.h"
using namespace llvm;
using namespace llvm::dxil;
constexpr StringLiteral DXILOpNamePrefix = "dx.op.";
namespace {
enum OverloadKind : uint16_t {
VOID = 1,
HALF = 1 << 1,
FLOAT = 1 << 2,
DOUBLE = 1 << 3,
I1 = 1 << 4,
I8 = 1 << 5,
I16 = 1 << 6,
I32 = 1 << 7,
I64 = 1 << 8,
UserDefineType = 1 << 9,
ObjectType = 1 << 10,
};
} // namespace
static const char *getOverloadTypeName(OverloadKind Kind) {
switch (Kind) {
case OverloadKind::HALF:
return "f16";
case OverloadKind::FLOAT:
return "f32";
case OverloadKind::DOUBLE:
return "f64";
case OverloadKind::I1:
return "i1";
case OverloadKind::I8:
return "i8";
case OverloadKind::I16:
return "i16";
case OverloadKind::I32:
return "i32";
case OverloadKind::I64:
return "i64";
case OverloadKind::VOID:
case OverloadKind::ObjectType:
case OverloadKind::UserDefineType:
break;
}
llvm_unreachable("invalid overload type for name");
return "void";
}
static OverloadKind getOverloadKind(Type *Ty) {
Type::TypeID T = Ty->getTypeID();
switch (T) {
case Type::VoidTyID:
return OverloadKind::VOID;
case Type::HalfTyID:
return OverloadKind::HALF;
case Type::FloatTyID:
return OverloadKind::FLOAT;
case Type::DoubleTyID:
return OverloadKind::DOUBLE;
case Type::IntegerTyID: {
IntegerType *ITy = cast<IntegerType>(Ty);
unsigned Bits = ITy->getBitWidth();
switch (Bits) {
case 1:
return OverloadKind::I1;
case 8:
return OverloadKind::I8;
case 16:
return OverloadKind::I16;
case 32:
return OverloadKind::I32;
case 64:
return OverloadKind::I64;
default:
llvm_unreachable("invalid overload type");
return OverloadKind::VOID;
}
}
case Type::PointerTyID:
return OverloadKind::UserDefineType;
case Type::StructTyID:
return OverloadKind::ObjectType;
default:
llvm_unreachable("invalid overload type");
return OverloadKind::VOID;
}
}
static std::string getTypeName(OverloadKind Kind, Type *Ty) {
if (Kind < OverloadKind::UserDefineType) {
return getOverloadTypeName(Kind);
} else if (Kind == OverloadKind::UserDefineType) {
StructType *ST = cast<StructType>(Ty);
return ST->getStructName().str();
} else if (Kind == OverloadKind::ObjectType) {
StructType *ST = cast<StructType>(Ty);
return ST->getStructName().str();
} else {
std::string Str;
raw_string_ostream OS(Str);
Ty->print(OS);
return OS.str();
}
}
// Static properties.
struct OpCodeProperty {
dxil::OpCode OpCode;
// Offset in DXILOpCodeNameTable.
unsigned OpCodeNameOffset;
dxil::OpCodeClass OpCodeClass;
// Offset in DXILOpCodeClassNameTable.
unsigned OpCodeClassNameOffset;
uint16_t OverloadTys;
llvm::Attribute::AttrKind FuncAttr;
int OverloadParamIndex; // parameter index which control the overload.
// When < 0, should be only 1 overload type.
unsigned NumOfParameters; // Number of parameters include return value.
unsigned ParameterTableOffset; // Offset in ParameterTable.
};
// Include getOpCodeClassName getOpCodeProperty, getOpCodeName and
// getOpCodeParameterKind which generated by tableGen.
#define DXIL_OP_OPERATION_TABLE
#include "DXILOperation.inc"
#undef DXIL_OP_OPERATION_TABLE
static std::string constructOverloadName(OverloadKind Kind, Type *Ty,
const OpCodeProperty &Prop) {
if (Kind == OverloadKind::VOID) {
return (Twine(DXILOpNamePrefix) + getOpCodeClassName(Prop)).str();
}
return (Twine(DXILOpNamePrefix) + getOpCodeClassName(Prop) + "." +
getTypeName(Kind, Ty))
.str();
}
static std::string constructOverloadTypeName(OverloadKind Kind,
StringRef TypeName) {
if (Kind == OverloadKind::VOID)
return TypeName.str();
assert(Kind < OverloadKind::UserDefineType && "invalid overload kind");
return (Twine(TypeName) + getOverloadTypeName(Kind)).str();
}
static StructType *getOrCreateStructType(StringRef Name,
ArrayRef<Type *> EltTys,
LLVMContext &Ctx) {
StructType *ST = StructType::getTypeByName(Ctx, Name);
if (ST)
return ST;
return StructType::create(Ctx, EltTys, Name);
}
static StructType *getResRetType(Type *OverloadTy, LLVMContext &Ctx) {
OverloadKind Kind = getOverloadKind(OverloadTy);
std::string TypeName = constructOverloadTypeName(Kind, "dx.types.ResRet.");
Type *FieldTypes[5] = {OverloadTy, OverloadTy, OverloadTy, OverloadTy,
Type::getInt32Ty(Ctx)};
return getOrCreateStructType(TypeName, FieldTypes, Ctx);
}
static StructType *getHandleType(LLVMContext &Ctx) {
return getOrCreateStructType("dx.types.Handle", Type::getInt8PtrTy(Ctx), Ctx);
}
static Type *getTypeFromParameterKind(ParameterKind Kind, Type *OverloadTy) {
auto &Ctx = OverloadTy->getContext();
switch (Kind) {
case ParameterKind::VOID:
return Type::getVoidTy(Ctx);
case ParameterKind::HALF:
return Type::getHalfTy(Ctx);
case ParameterKind::FLOAT:
return Type::getFloatTy(Ctx);
case ParameterKind::DOUBLE:
return Type::getDoubleTy(Ctx);
case ParameterKind::I1:
return Type::getInt1Ty(Ctx);
case ParameterKind::I8:
return Type::getInt8Ty(Ctx);
case ParameterKind::I16:
return Type::getInt16Ty(Ctx);
case ParameterKind::I32:
return Type::getInt32Ty(Ctx);
case ParameterKind::I64:
return Type::getInt64Ty(Ctx);
case ParameterKind::OVERLOAD:
return OverloadTy;
case ParameterKind::RESOURCE_RET:
return getResRetType(OverloadTy, Ctx);
case ParameterKind::DXIL_HANDLE:
return getHandleType(Ctx);
default:
break;
}
llvm_unreachable("Invalid parameter kind");
return nullptr;
}
static FunctionType *getDXILOpFunctionType(const OpCodeProperty *Prop,
Type *OverloadTy) {
SmallVector<Type *> ArgTys;
auto ParamKinds = getOpCodeParameterKind(*Prop);
for (unsigned I = 0; I < Prop->NumOfParameters; ++I) {
ParameterKind Kind = ParamKinds[I];
ArgTys.emplace_back(getTypeFromParameterKind(Kind, OverloadTy));
}
return FunctionType::get(
ArgTys[0], ArrayRef<Type *>(&ArgTys[1], ArgTys.size() - 1), false);
}
static FunctionCallee getOrCreateDXILOpFunction(dxil::OpCode DXILOp,
Type *OverloadTy, Module &M) {
const OpCodeProperty *Prop = getOpCodeProperty(DXILOp);
OverloadKind Kind = getOverloadKind(OverloadTy);
// FIXME: find the issue and report error in clang instead of check it in
// backend.
if ((Prop->OverloadTys & (uint16_t)Kind) == 0) {
llvm_unreachable("invalid overload");
}
std::string FnName = constructOverloadName(Kind, OverloadTy, *Prop);
// Dependent on name to dedup.
if (auto *Fn = M.getFunction(FnName))
return FunctionCallee(Fn);
FunctionType *DXILOpFT = getDXILOpFunctionType(Prop, OverloadTy);
return M.getOrInsertFunction(FnName, DXILOpFT);
}
namespace llvm {
namespace dxil {
CallInst *DXILOpBuilder::createDXILOpCall(dxil::OpCode OpCode, Type *OverloadTy,
llvm::iterator_range<Use *> Args) {
auto Fn = getOrCreateDXILOpFunction(OpCode, OverloadTy, M);
SmallVector<Value *> FullArgs;
FullArgs.emplace_back(B.getInt32((int32_t)OpCode));
FullArgs.append(Args.begin(), Args.end());
return B.CreateCall(Fn, FullArgs);
}
Type *DXILOpBuilder::getOverloadTy(dxil::OpCode OpCode, FunctionType *FT,
bool NoOpCodeParam) {
const OpCodeProperty *Prop = getOpCodeProperty(OpCode);
if (Prop->OverloadParamIndex < 0) {
auto &Ctx = FT->getContext();
// When only has 1 overload type, just return it.
switch (Prop->OverloadTys) {
case OverloadKind::VOID:
return Type::getVoidTy(Ctx);
case OverloadKind::HALF:
return Type::getHalfTy(Ctx);
case OverloadKind::FLOAT:
return Type::getFloatTy(Ctx);
case OverloadKind::DOUBLE:
return Type::getDoubleTy(Ctx);
case OverloadKind::I1:
return Type::getInt1Ty(Ctx);
case OverloadKind::I8:
return Type::getInt8Ty(Ctx);
case OverloadKind::I16:
return Type::getInt16Ty(Ctx);
case OverloadKind::I32:
return Type::getInt32Ty(Ctx);
case OverloadKind::I64:
return Type::getInt64Ty(Ctx);
default:
llvm_unreachable("invalid overload type");
return nullptr;
}
}
// Prop->OverloadParamIndex is 0, overload type is FT->getReturnType().
Type *OverloadType = FT->getReturnType();
if (Prop->OverloadParamIndex != 0) {
// Skip Return Type and Type for DXIL opcode.
const unsigned SkipedParam = NoOpCodeParam ? 2 : 1;
OverloadType = FT->getParamType(Prop->OverloadParamIndex - SkipedParam);
}
auto ParamKinds = getOpCodeParameterKind(*Prop);
auto Kind = ParamKinds[Prop->OverloadParamIndex];
// For ResRet and CBufferRet, OverloadTy is in field of StructType.
if (Kind == ParameterKind::CBUFFER_RET ||
Kind == ParameterKind::RESOURCE_RET) {
auto *ST = cast<StructType>(OverloadType);
OverloadType = ST->getElementType(0);
}
return OverloadType;
}
const char *DXILOpBuilder::getOpCodeName(dxil::OpCode DXILOp) {
return ::getOpCodeName(DXILOp);
}
} // namespace dxil
} // namespace llvm