| // Copyright 2021 The IREE Authors |
| // |
| // Licensed 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 |
| |
| #include "compiler/plugins/target/LLVMCPU/LibraryBuilder.h" |
| |
| #include "llvm/IR/DerivedTypes.h" |
| #include "llvm/IR/IRBuilder.h" |
| |
| // ============================================================================= |
| // |
| // NOTE: these structures model 1:1 those in iree/hal/local/executable_library.h |
| // |
| // This file must always track the latest version. Backwards compatibility with |
| // existing runtimes using older versions of the header is maintained by |
| // emitting variants of the structs matching those in the older headers and |
| // selecting between them in the query function based on the requested version. |
| // |
| // ============================================================================= |
| |
| namespace mlir::iree_compiler::IREE::HAL { |
| |
| static inline int64_t roundUpToAlignment(int64_t value, int64_t alignment) { |
| return (value + (alignment - 1)) & ~(alignment - 1); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // iree/hal/local/executable_library.h structure types |
| //===----------------------------------------------------------------------===// |
| // The IR snippets below were pulled from clang running with `-S -emit-llvm` |
| // on the executable_library.h header: https://godbolt.org/z/6bMv5jfvf |
| |
| // %struct.iree_hal_executable_import_table_v0_t = type { |
| // i32, |
| // i8** |
| // } |
| static llvm::StructType *makeImportTableType(llvm::LLVMContext &context) { |
| if (auto *existingType = llvm::StructType::getTypeByName( |
| context, "iree_hal_executable_import_table_v0_t")) { |
| return existingType; |
| } |
| auto *i32Type = llvm::IntegerType::getInt32Ty(context); |
| auto *i8PtrType = llvm::PointerType::getUnqual(context); |
| auto *type = |
| llvm::StructType::create(context, |
| { |
| i32Type, |
| llvm::PointerType::get(i8PtrType, 0), |
| }, |
| "iree_hal_executable_import_table_v0_t", |
| /*isPacked=*/false); |
| return type; |
| } |
| |
| // %struct.iree_hal_executable_environment_v0_t = type { |
| // ... |
| // } |
| static llvm::StructType *makeEnvironmentType(llvm::LLVMContext &context) { |
| auto *type = llvm::StructType::getTypeByName( |
| context, "iree_hal_executable_environment_v0_t"); |
| if (!type) { |
| type = llvm::StructType::create(context, |
| "iree_hal_executable_environment_v0_t"); |
| } |
| return type; |
| } |
| |
| // %struct.iree_hal_executable_dispatch_state_v0_t = type { |
| // ... |
| // } |
| static llvm::StructType *makeDispatchStateType(llvm::LLVMContext &context) { |
| auto *type = llvm::StructType::getTypeByName( |
| context, "iree_hal_executable_dispatch_state_v0_t"); |
| if (!type) { |
| type = llvm::StructType::create(context, |
| "iree_hal_executable_dispatch_state_v0_t"); |
| } |
| return type; |
| } |
| |
| // %struct.iree_hal_executable_workgroup_state_v0_t = type { |
| // ... |
| // } |
| static llvm::StructType *makeWorkgroupStateType(llvm::LLVMContext &context) { |
| auto *type = llvm::StructType::getTypeByName( |
| context, "iree_hal_executable_workgroup_state_v0_t"); |
| if (!type) { |
| type = llvm::StructType::create(context, |
| "iree_hal_executable_workgroup_state_v0_t"); |
| } |
| return type; |
| } |
| |
| // i32 (%struct.iree_hal_executable_environment_v0_t*, |
| // %struct.iree_hal_executable_dispatch_state_v0_t*, |
| // i8*) |
| static llvm::FunctionType * |
| makeDispatchFunctionType(llvm::LLVMContext &context) { |
| auto *environmentType = makeEnvironmentType(context); |
| auto *dispatchStateType = makeDispatchStateType(context); |
| auto *workgroupStateType = makeWorkgroupStateType(context); |
| auto *i32Type = llvm::IntegerType::getInt32Ty(context); |
| return llvm::FunctionType::get( |
| i32Type, |
| { |
| llvm::PointerType::get(environmentType, 0), |
| llvm::PointerType::get(dispatchStateType, 0), |
| llvm::PointerType::get(workgroupStateType, 0), |
| }, |
| /*isVarArg=*/false); |
| } |
| |
| // %struct.iree_hal_executable_dispatch_attrs_v0_t = type { |
| // i16, |
| // i8, |
| // i8, |
| // i32, |
| // i64[8] |
| // } |
| static llvm::StructType *makeDispatchAttrsType(llvm::LLVMContext &context) { |
| if (auto *existingType = llvm::StructType::getTypeByName( |
| context, "iree_hal_executable_dispatch_attrs_v0_t")) { |
| return existingType; |
| } |
| auto *i8Type = llvm::IntegerType::getInt8Ty(context); |
| auto *i16Type = llvm::IntegerType::getInt16Ty(context); |
| auto *i32Type = llvm::IntegerType::getInt32Ty(context); |
| auto *i64Type = llvm::IntegerType::getInt64Ty(context); |
| auto *type = |
| llvm::StructType::create(context, |
| { |
| i16Type, i8Type, i8Type, i32Type, |
| i64Type, // [0] |
| i64Type, // [1] |
| i64Type, // [2] |
| i64Type, // [3] |
| i64Type, // [4] |
| i64Type, // [5] |
| i64Type, // [6] |
| i64Type, // [7] |
| }, |
| "iree_hal_executable_dispatch_attrs_v0_t", |
| /*isPacked=*/false); |
| return type; |
| } |
| |
| // %struct.iree_hal_executable_source_location_v0_t = type { |
| // i32, |
| // i32, |
| // i8* |
| // } |
| static llvm::StructType *makeSourceLocationType(llvm::LLVMContext &context) { |
| if (auto *existingType = llvm::StructType::getTypeByName( |
| context, "iree_hal_executable_source_location_v0_t")) { |
| return existingType; |
| } |
| auto *i32Type = llvm::IntegerType::getInt32Ty(context); |
| auto *i8PtrType = llvm::PointerType::getUnqual(context); |
| auto *type = |
| llvm::StructType::create(context, |
| { |
| i32Type, |
| i32Type, |
| i8PtrType, |
| }, |
| "iree_hal_executable_source_location_v0_t", |
| /*isPacked=*/false); |
| return type; |
| } |
| |
| // %struct.iree_hal_executable_stage_location_table_v0_t = type { |
| // i32, |
| // i8**, |
| // %struct.iree_hal_executable_source_location_v0_t*, |
| // } |
| static llvm::StructType * |
| makeStageLocationTableType(llvm::LLVMContext &context) { |
| if (auto *existingType = llvm::StructType::getTypeByName( |
| context, "iree_hal_executable_stage_location_table_v0_t")) { |
| return existingType; |
| } |
| auto *i32Type = llvm::IntegerType::getInt32Ty(context); |
| auto *i8PtrType = llvm::PointerType::getUnqual(context); |
| auto *sourceLocationType = makeSourceLocationType(context); |
| auto *type = llvm::StructType::create( |
| context, |
| { |
| i32Type, |
| llvm::PointerType::get(i8PtrType, 0), |
| llvm::PointerType::get(sourceLocationType, 0), |
| }, |
| "iree_hal_executable_stage_location_table_v0_t", |
| /*isPacked=*/false); |
| return type; |
| } |
| |
| // %struct.iree_hal_executable_export_table_v0_t = type { |
| // i32, |
| // i32*, |
| // %struct.iree_hal_executable_dispatch_attrs_v0_t*, |
| // i8**, |
| // i8**, |
| // %struct.iree_hal_executable_source_location_v0_t*, |
| // %struct.iree_hal_executable_stage_location_table_v0_t*, |
| // } |
| static llvm::StructType *makeExportTableType(llvm::LLVMContext &context) { |
| if (auto *existingType = llvm::StructType::getTypeByName( |
| context, "iree_hal_executable_export_table_v0_t")) { |
| return existingType; |
| } |
| auto *i32Type = llvm::IntegerType::getInt32Ty(context); |
| auto *dispatchFunctionType = makeDispatchFunctionType(context); |
| auto *dispatchFunctionPointerType = |
| llvm::PointerType::get(dispatchFunctionType, 0); |
| auto *dispatchAttrsType = makeDispatchAttrsType(context); |
| auto *i8PtrType = llvm::PointerType::getUnqual(context); |
| auto *sourceLocationType = makeSourceLocationType(context); |
| auto *stageLocationTableType = makeStageLocationTableType(context); |
| auto *type = llvm::StructType::create( |
| context, |
| { |
| i32Type, |
| llvm::PointerType::get(dispatchFunctionPointerType, 0), |
| llvm::PointerType::get(dispatchAttrsType, 0), |
| llvm::PointerType::get(i8PtrType, 0), |
| llvm::PointerType::get(i8PtrType, 0), |
| llvm::PointerType::get(sourceLocationType, 0), |
| llvm::PointerType::get(stageLocationTableType, 0), |
| }, |
| "iree_hal_executable_export_table_v0_t", |
| /*isPacked=*/false); |
| return type; |
| } |
| |
| // %struct.iree_hal_executable_constant_table_v0_t = type { |
| // i32 |
| // } |
| static llvm::StructType *makeConstantTableType(llvm::LLVMContext &context) { |
| if (auto *existingType = llvm::StructType::getTypeByName( |
| context, "iree_hal_executable_constant_table_v0_t")) { |
| return existingType; |
| } |
| auto *i32Type = llvm::IntegerType::getInt32Ty(context); |
| auto *type = |
| llvm::StructType::create(context, |
| { |
| i32Type, |
| }, |
| "iree_hal_executable_constant_table_v0_t", |
| /*isPacked=*/false); |
| return type; |
| } |
| |
| // %struct.iree_hal_executable_source_file_v0_t = type { |
| // i32, |
| // i8*, |
| // i32, |
| // i8* |
| // } |
| static llvm::StructType *makeSourceFileType(llvm::LLVMContext &context) { |
| if (auto *existingType = llvm::StructType::getTypeByName( |
| context, "iree_hal_executable_source_file_v0_t")) { |
| return existingType; |
| } |
| auto *i32Type = llvm::IntegerType::getInt32Ty(context); |
| auto *i8PtrType = llvm::PointerType::getUnqual(context); |
| auto *type = llvm::StructType::create(context, |
| { |
| i32Type, |
| i8PtrType, |
| i32Type, |
| i8PtrType, |
| }, |
| "iree_hal_executable_source_file_v0_t", |
| /*isPacked=*/false); |
| return type; |
| } |
| |
| // %struct.iree_hal_executable_source_file_table_v0_t = type { |
| // i32, |
| // %struct.iree_hal_executable_source_file_v0_t*, |
| // } |
| static llvm::StructType *makeSourceTableType(llvm::LLVMContext &context) { |
| if (auto *existingType = llvm::StructType::getTypeByName( |
| context, "iree_hal_executable_source_file_table_v0_t")) { |
| return existingType; |
| } |
| auto *i32Type = llvm::IntegerType::getInt32Ty(context); |
| auto *sourceFileType = makeSourceFileType(context); |
| auto *type = |
| llvm::StructType::create(context, |
| { |
| i32Type, |
| llvm::PointerType::get(sourceFileType, 0), |
| }, |
| "iree_hal_executable_source_file_table_v0_t", |
| /*isPacked=*/false); |
| return type; |
| } |
| |
| // %struct.iree_hal_executable_library_header_t = type { |
| // i32, |
| // i8*, |
| // i32, |
| // i32 |
| // } |
| static llvm::StructType *makeLibraryHeaderType(llvm::LLVMContext &context) { |
| if (auto *existingType = llvm::StructType::getTypeByName( |
| context, "iree_hal_executable_library_header_t")) { |
| return existingType; |
| } |
| auto *i32Type = llvm::IntegerType::getInt32Ty(context); |
| auto *i8PtrType = llvm::PointerType::getUnqual(context); |
| auto *type = llvm::StructType::create(context, |
| { |
| i32Type, |
| i8PtrType, |
| i32Type, |
| i32Type, |
| }, |
| "iree_hal_executable_library_header_t", |
| /*isPacked=*/false); |
| return type; |
| } |
| |
| // %struct.iree_hal_executable_library_v0_t = type { |
| // %struct.iree_hal_executable_library_header_t*, |
| // %struct.iree_hal_executable_import_table_v0_t, |
| // %struct.iree_hal_executable_export_table_v0_t, |
| // } |
| static llvm::StructType *makeLibraryType(llvm::StructType *libraryHeaderType) { |
| auto &context = libraryHeaderType->getContext(); |
| if (auto *existingType = llvm::StructType::getTypeByName( |
| context, "iree_hal_executable_library_v0_t")) { |
| return existingType; |
| } |
| auto *importTableType = makeImportTableType(context); |
| auto *exportTableType = makeExportTableType(context); |
| auto *constantTableType = makeConstantTableType(context); |
| auto *sourceTableType = makeSourceTableType(context); |
| auto *type = |
| llvm::StructType::create(context, |
| { |
| llvm::PointerType::get(libraryHeaderType, 0), |
| importTableType, |
| exportTableType, |
| constantTableType, |
| sourceTableType, |
| }, |
| "iree_hal_executable_library_v0_t", |
| /*isPacked=*/false); |
| return type; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // IR construction utilities |
| //===----------------------------------------------------------------------===// |
| |
| // Creates a global NUL-terminated string constant. |
| // |
| // Example: |
| // @.str.2 = private unnamed_addr constant [6 x i8] c"lib_a\00", align 1 |
| static llvm::Constant *createStringConstant(StringRef value, |
| llvm::Module *module) { |
| auto i8Type = llvm::IntegerType::getInt8Ty(module->getContext()); |
| auto i32Type = llvm::IntegerType::getInt32Ty(module->getContext()); |
| auto *stringType = llvm::ArrayType::get(i8Type, value.size() + /*NUL*/ 1); |
| auto *literal = |
| llvm::ConstantDataArray::getString(module->getContext(), value); |
| auto *global = new llvm::GlobalVariable(*module, stringType, |
| /*isConstant=*/true, |
| llvm::GlobalVariable::PrivateLinkage, |
| literal, /*Name=*/""); |
| global->setAlignment(llvm::MaybeAlign(1)); |
| llvm::Constant *zero = llvm::ConstantInt::get(i32Type, 0); |
| return llvm::ConstantExpr::getInBoundsGetElementPtr( |
| stringType, global, ArrayRef<llvm::Constant *>{zero, zero}); |
| } |
| |
| // Creates a global NUL-terminated string constant or NULL if the string is |
| // empty. |
| static llvm::Constant *createStringConstantOrNull(StringRef value, |
| llvm::Module *module) { |
| if (value.empty()) { |
| auto i8Type = llvm::IntegerType::getInt8Ty(module->getContext()); |
| return llvm::ConstantPointerNull::get(llvm::PointerType::get(i8Type, 0)); |
| } |
| return createStringConstant(value, module); |
| } |
| |
| // Creates a global serialized buffer constant (or string without NUL). |
| // |
| // Example: |
| // @.data = private unnamed_addr constant [5 x i8] c"lib_a", align 1 |
| static llvm::Constant *createBufferConstant(StringRef name, |
| ArrayRef<char> value, |
| llvm::Module *module) { |
| auto i8Type = llvm::IntegerType::getInt8Ty(module->getContext()); |
| auto i32Type = llvm::IntegerType::getInt32Ty(module->getContext()); |
| auto *bufferType = llvm::ArrayType::get(i8Type, value.size()); |
| auto *literal = llvm::ConstantDataArray::get(module->getContext(), value); |
| auto *global = new llvm::GlobalVariable( |
| *module, bufferType, |
| /*isConstant=*/true, llvm::GlobalVariable::PrivateLinkage, literal, name); |
| global->setAlignment(llvm::MaybeAlign(1)); |
| llvm::Constant *zero = llvm::ConstantInt::get(i32Type, 0); |
| return llvm::ConstantExpr::getInBoundsGetElementPtr( |
| bufferType, global, ArrayRef<llvm::Constant *>{zero, zero}); |
| } |
| |
| // Creates a global constant with the given elements. |
| static llvm::Constant *createArrayConstant(StringRef name, |
| llvm::Type *elementType, |
| ArrayRef<llvm::Constant *> elements, |
| llvm::Module *module) { |
| auto *i32Type = llvm::IntegerType::getInt32Ty(module->getContext()); |
| llvm::Constant *zero = llvm::ConstantInt::get(i32Type, 0); |
| auto *arrayType = llvm::ArrayType::get(elementType, elements.size()); |
| auto *global = new llvm::GlobalVariable( |
| *module, arrayType, /*isConstant=*/true, |
| llvm::GlobalVariable::PrivateLinkage, |
| llvm::ConstantArray::get(arrayType, elements), name); |
| return llvm::ConstantExpr::getInBoundsGetElementPtr( |
| arrayType, global, ArrayRef<llvm::Constant *>{zero, zero}); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Builder interface |
| //===----------------------------------------------------------------------===// |
| |
| llvm::Function *LibraryBuilder::build(StringRef queryFuncName) { |
| auto &context = module->getContext(); |
| auto *i32Type = llvm::IntegerType::getInt32Ty(context); |
| auto *environmentStructType = makeEnvironmentType(context); |
| auto *environmentType = llvm::PointerType::get(environmentStructType, 0); |
| auto *libraryHeaderType = makeLibraryHeaderType(context); |
| |
| // %struct.iree_hal_executable_library_header_t** |
| // @iree_hal_library_query(i32, %struct.iree_hal_executable_environment_v0_t*) |
| auto *queryFuncType = |
| llvm::FunctionType::get(llvm::PointerType::get(libraryHeaderType, 0), |
| { |
| i32Type, |
| environmentType, |
| }, |
| /*isVarArg=*/false); |
| auto *func = |
| llvm::Function::Create(queryFuncType, llvm::GlobalValue::InternalLinkage, |
| queryFuncName, *module); |
| |
| auto *entryBlock = llvm::BasicBlock::Create(context, "entry", func); |
| llvm::IRBuilder<> builder(entryBlock); |
| |
| // Build out the header for each version and select it at runtime. |
| // NOTE: today there is just one version so this is rather simple: |
| // return max_version == 0 ? &library : NULL; |
| auto *v0 = buildLibraryV0((queryFuncName + "_v0").str()); |
| builder.CreateRet(builder.CreateSelect( |
| builder.CreateICmpEQ(func->getArg(0), |
| llvm::ConstantInt::get( |
| i32Type, static_cast<int64_t>(Version::LATEST))), |
| builder.CreatePointerCast(v0, |
| llvm::PointerType::get(libraryHeaderType, 0)), |
| llvm::ConstantPointerNull::get( |
| llvm::PointerType::get(libraryHeaderType, 0)))); |
| |
| return func; |
| } |
| |
| llvm::Constant * |
| LibraryBuilder::buildLibraryV0ImportTable(std::string libraryName) { |
| auto &context = module->getContext(); |
| auto *importTableType = makeImportTableType(context); |
| auto *i8Type = llvm::IntegerType::getInt8Ty(context); |
| auto *i32Type = llvm::IntegerType::getInt32Ty(context); |
| llvm::Constant *symbolNames = |
| llvm::Constant::getNullValue(llvm::PointerType::get(i8Type, 0)); |
| if (!imports.empty()) { |
| SmallVector<llvm::Constant *> symbolNameValues; |
| for (auto &import : imports) { |
| auto symbolName = import.symbol_name; |
| if (import.weak) |
| symbolName = "?" + symbolName; |
| symbolNameValues.push_back(createStringConstant(symbolName, module)); |
| } |
| symbolNames = createArrayConstant(libraryName + "_import_names", |
| llvm::PointerType::get(i8Type, 0), |
| symbolNameValues, module); |
| } |
| return llvm::ConstantStruct::get( |
| importTableType, { |
| // count= |
| llvm::ConstantInt::get(i32Type, imports.size()), |
| // symbols= |
| symbolNames, |
| }); |
| } |
| |
| llvm::Constant * |
| LibraryBuilder::buildLibraryV0ExportTable(std::string libraryName) { |
| auto &context = module->getContext(); |
| auto *exportTableType = makeExportTableType(context); |
| auto *dispatchFunctionType = makeDispatchFunctionType(context); |
| auto *dispatchAttrsType = makeDispatchAttrsType(context); |
| auto *sourceLocationType = makeSourceLocationType(context); |
| auto *stageLocationTableType = makeStageLocationTableType(context); |
| auto *i8Type = llvm::IntegerType::getInt8Ty(context); |
| auto *i16Type = llvm::IntegerType::getInt16Ty(context); |
| auto *i32Type = llvm::IntegerType::getInt32Ty(context); |
| auto *i64Type = llvm::IntegerType::getInt64Ty(context); |
| |
| // iree_hal_executable_export_table_v0_t::ptrs |
| SmallVector<llvm::Constant *> exportPtrValues; |
| for (auto dispatch : exports) |
| exportPtrValues.push_back(dispatch.func); |
| llvm::Constant *exportPtrs = createArrayConstant( |
| libraryName + "_funcs", llvm::PointerType::get(dispatchFunctionType, 0), |
| exportPtrValues, module); |
| |
| // iree_hal_executable_export_table_v0_t::attrs |
| llvm::Constant *exportAttrs = |
| llvm::Constant::getNullValue(llvm::PointerType::get(i32Type, 0)); |
| bool hasNonDefaultAttrs = llvm::any_of(exports, [](const auto &dispatch) { |
| return !dispatch.attrs.isDefault(); |
| }); |
| if (hasNonDefaultAttrs) { |
| SmallVector<llvm::Constant *> exportAttrValues; |
| for (auto dispatch : exports) { |
| exportAttrValues.push_back(llvm::ConstantStruct::get( |
| dispatchAttrsType, |
| { |
| // local_memory_pages= |
| llvm::ConstantInt::get( |
| i16Type, roundUpToAlignment(dispatch.attrs.localMemorySize, |
| kWorkgroupLocalMemoryPageSize) / |
| kWorkgroupLocalMemoryPageSize), |
| // constant_count= |
| llvm::ConstantInt::get(i8Type, dispatch.attrs.constantCount), |
| // binding_count= |
| llvm::ConstantInt::get(i8Type, dispatch.attrs.bindingCount), |
| // reserved_0= |
| llvm::ConstantInt::get(i32Type, 0), |
| // reserved_1[0]= |
| llvm::ConstantInt::get(i64Type, 0), |
| // reserved_1[1]= |
| llvm::ConstantInt::get(i64Type, 0), |
| // reserved_1[2]= |
| llvm::ConstantInt::get(i64Type, 0), |
| // reserved_1[3]= |
| llvm::ConstantInt::get(i64Type, 0), |
| // reserved_1[4]= |
| llvm::ConstantInt::get(i64Type, 0), |
| // reserved_1[5]= |
| llvm::ConstantInt::get(i64Type, 0), |
| // reserved_1[6]= |
| llvm::ConstantInt::get(i64Type, 0), |
| // reserved_1[7]= |
| llvm::ConstantInt::get(i64Type, 0), |
| })); |
| } |
| exportAttrs = createArrayConstant(libraryName + "_attrs", dispatchAttrsType, |
| exportAttrValues, module); |
| } |
| |
| // iree_hal_executable_export_table_v0_t::names |
| llvm::Constant *exportNames = |
| llvm::Constant::getNullValue(llvm::PointerType::get(i8Type, 0)); |
| if (mode == Mode::INCLUDE_REFLECTION_ATTRS) { |
| SmallVector<llvm::Constant *> exportNameValues; |
| for (auto dispatch : exports) |
| exportNameValues.push_back(createStringConstant(dispatch.name, module)); |
| exportNames = createArrayConstant(libraryName + "_names", |
| llvm::PointerType::get(i8Type, 0), |
| exportNameValues, module); |
| } |
| |
| // iree_hal_executable_export_table_v0_t::tags |
| auto *i8PtrType = llvm::PointerType::get(i8Type, 0); |
| llvm::Constant *exportTags = |
| llvm::Constant::getNullValue(llvm::PointerType::get(i8PtrType, 0)); |
| bool hasAnyTags = llvm::any_of( |
| exports, [](auto &dispatch) { return !dispatch.tag.empty(); }); |
| if (mode == Mode::INCLUDE_REFLECTION_ATTRS && hasAnyTags) { |
| SmallVector<llvm::Constant *> exportTagValues; |
| for (auto dispatch : exports) |
| exportTagValues.push_back( |
| createStringConstantOrNull(dispatch.tag, module)); |
| exportTags = createArrayConstant(libraryName + "_tags", |
| llvm::PointerType::get(i8Type, 0), |
| exportTagValues, module); |
| } |
| |
| // iree_hal_executable_export_table_v0_t::source_locations |
| llvm::Constant *exportSourceLocations = llvm::Constant::getNullValue( |
| llvm::PointerType::get(sourceLocationType, 0)); |
| if (mode == Mode::INCLUDE_REFLECTION_ATTRS) { |
| SmallVector<llvm::Constant *> exportSourceLocationValues; |
| for (auto dispatch : exports) { |
| exportSourceLocationValues.push_back(llvm::ConstantStruct::get( |
| sourceLocationType, |
| { |
| // line= |
| llvm::ConstantInt::get(i32Type, dispatch.sourceLocation.line), |
| // path_length= |
| llvm::ConstantInt::get(i32Type, |
| dispatch.sourceLocation.path.size()), |
| // path= |
| createStringConstant(dispatch.sourceLocation.path, module), |
| })); |
| } |
| exportSourceLocations = createArrayConstant( |
| libraryName + "_source_locations", sourceLocationType, |
| exportSourceLocationValues, module); |
| } |
| |
| // iree_hal_executable_export_table_v0_t::stage_locations |
| llvm::Constant *exportStageLocations = llvm::Constant::getNullValue( |
| llvm::PointerType::get(stageLocationTableType, 0)); |
| if (mode == Mode::INCLUDE_REFLECTION_ATTRS) { |
| SmallVector<llvm::Constant *> exportStageTableValues; |
| for (auto dispatch : exports) { |
| SmallVector<llvm::Constant *> exportStageNameValues; |
| SmallVector<llvm::Constant *> exportSourceLocationValues; |
| for (auto &stageLocation : dispatch.stageLocations) { |
| exportStageNameValues.push_back( |
| createStringConstant(stageLocation.stage, module)); |
| exportSourceLocationValues.push_back(llvm::ConstantStruct::get( |
| sourceLocationType, |
| { |
| // line= |
| llvm::ConstantInt::get(i32Type, stageLocation.line), |
| // path_length= |
| llvm::ConstantInt::get(i32Type, stageLocation.path.size()), |
| // path= |
| createStringConstant(stageLocation.path, module), |
| })); |
| } |
| llvm::Constant *stageNamesPtr = createArrayConstant( |
| libraryName + "_" + dispatch.name + "_stage_names", |
| llvm::PointerType::get(i8Type, 0), exportStageNameValues, module); |
| llvm::Constant *sourceLocationsPtr = createArrayConstant( |
| libraryName + "_" + dispatch.name + "_stage_source_locations", |
| sourceLocationType, exportSourceLocationValues, module); |
| exportStageTableValues.push_back(llvm::ConstantStruct::get( |
| stageLocationTableType, |
| { |
| // count= |
| llvm::ConstantInt::get(i32Type, exportStageNameValues.size()), |
| // names= |
| stageNamesPtr, |
| // locations= |
| sourceLocationsPtr, |
| })); |
| } |
| if (!exportStageTableValues.empty()) { |
| exportStageLocations = createArrayConstant( |
| libraryName + "_stage_location_tables", stageLocationTableType, |
| exportStageTableValues, module); |
| } |
| } |
| |
| return llvm::ConstantStruct::get( |
| exportTableType, { |
| // count= |
| llvm::ConstantInt::get(i32Type, exports.size()), |
| // ptrs= |
| exportPtrs, |
| // attrs= |
| exportAttrs, |
| // names= |
| exportNames, |
| // tags= |
| exportTags, |
| // source_locations= |
| exportSourceLocations, |
| // stage_locations= |
| exportStageLocations, |
| }); |
| } |
| |
| llvm::Constant * |
| LibraryBuilder::buildLibraryV0ConstantTable(std::string libraryName) { |
| auto &context = module->getContext(); |
| auto *constantTableType = makeConstantTableType(context); |
| auto *i32Type = llvm::IntegerType::getInt32Ty(context); |
| return llvm::ConstantStruct::get( |
| constantTableType, { |
| // count= |
| llvm::ConstantInt::get(i32Type, constantCount), |
| }); |
| } |
| |
| llvm::Constant * |
| LibraryBuilder::buildLibraryV0SourceTable(std::string libraryName) { |
| auto &context = module->getContext(); |
| auto *sourceFileType = makeSourceFileType(context); |
| auto *sourceTableType = makeSourceTableType(context); |
| auto *i32Type = llvm::IntegerType::getInt32Ty(context); |
| llvm::Constant *sourceFilesValue = |
| llvm::Constant::getNullValue(llvm::PointerType::get(sourceFileType, 0)); |
| if (!sourceFiles.empty()) { |
| SmallVector<llvm::Constant *> sourceFileValues; |
| for (auto &sourceFile : sourceFiles) { |
| sourceFileValues.push_back(llvm::ConstantStruct::get( |
| sourceFileType, |
| { |
| // path_length= |
| llvm::ConstantInt::get(i32Type, sourceFile.path.size()), |
| // path= |
| createStringConstant(sourceFile.path, module), |
| // content_length= |
| llvm::ConstantInt::get(i32Type, sourceFile.contents.size()), |
| // content= |
| createBufferConstant(sourceFile.path, sourceFile.contents, |
| module), |
| })); |
| } |
| sourceFilesValue = createArrayConstant( |
| libraryName + "_sources", sourceFileType, sourceFileValues, module); |
| } |
| return llvm::ConstantStruct::get( |
| sourceTableType, { |
| // count= |
| llvm::ConstantInt::get(i32Type, sourceFiles.size()), |
| // files= |
| sourceFilesValue, |
| }); |
| } |
| |
| llvm::Constant *LibraryBuilder::buildLibraryV0(std::string libraryName) { |
| auto &context = module->getContext(); |
| auto *libraryHeaderType = makeLibraryHeaderType(context); |
| auto *libraryType = makeLibraryType(libraryHeaderType); |
| auto *i32Type = llvm::IntegerType::getInt32Ty(context); |
| |
| // ----- Header ----- |
| |
| auto *libraryHeader = new llvm::GlobalVariable( |
| *module, libraryHeaderType, /*isConstant=*/true, |
| llvm::GlobalVariable::PrivateLinkage, |
| llvm::ConstantStruct::get( |
| libraryHeaderType, |
| { |
| // version= |
| llvm::ConstantInt::get(i32Type, |
| static_cast<int64_t>(Version::LATEST)), |
| // name= |
| createStringConstant(module->getName(), module), |
| // features= |
| llvm::ConstantInt::get(i32Type, static_cast<int64_t>(features)), |
| // sanitizer= |
| llvm::ConstantInt::get(i32Type, |
| static_cast<int64_t>(sanitizerKind)), |
| }), |
| /*Name=*/libraryName + "_header"); |
| // TODO(benvanik): force alignment (8? natural pointer width?) |
| |
| // ----- Library ----- |
| |
| auto *library = new llvm::GlobalVariable( |
| *module, libraryType, /*isConstant=*/true, |
| llvm::GlobalVariable::PrivateLinkage, |
| llvm::ConstantStruct::get(libraryType, |
| { |
| // header= |
| libraryHeader, |
| // imports= |
| buildLibraryV0ImportTable(libraryName), |
| // exports= |
| buildLibraryV0ExportTable(libraryName), |
| // constants= |
| buildLibraryV0ConstantTable(libraryName), |
| // sources= |
| buildLibraryV0SourceTable(libraryName), |
| }), |
| /*Name=*/libraryName); |
| // TODO(benvanik): force alignment (8? natural pointer width?) |
| |
| return library; |
| } |
| |
| } // namespace mlir::iree_compiler::IREE::HAL |