Skip to content

Commit

Permalink
Adding a skeleton librt to provide libc/libm/etc during embedded link…
Browse files Browse the repository at this point in the history
…ing. (iree-org#6560)

The code should be factored out as we start to add things like placeholder
functions for querying runtime state/etc but starting simple here to get
us bootstrapped.

See librt/src/librt.h for some notes.
Run librt/build.sh to update the .bc file and then rebuild iree-* so the
new version is pulled in.
  • Loading branch information
benvanik authored Jul 27, 2021
1 parent 628877c commit 69afdd2
Show file tree
Hide file tree
Showing 12 changed files with 309 additions and 11 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,6 @@ local.properties
# Generated documentation files
mkdocs/site/
docs/website/site/

# Temporary files
iree/compiler/Dialect/HAL/Target/LLVM/librt/bin/librt.ll
2 changes: 2 additions & 0 deletions iree/compiler/Dialect/HAL/Target/LLVM/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,14 @@ cc_library(
"//iree/compiler/Codegen/LLVMCPU",
"//iree/compiler/Codegen/Utils",
"//iree/compiler/Dialect/HAL/Target",
"//iree/compiler/Dialect/HAL/Target/LLVM/librt",
"//iree/compiler/Utils",
"//iree/schemas:dylib_executable_def_c_fbs",
"@llvm-project//llvm:AArch64AsmParser",
"@llvm-project//llvm:AArch64CodeGen",
"@llvm-project//llvm:ARMAsmParser",
"@llvm-project//llvm:ARMCodeGen",
"@llvm-project//llvm:BitReader",
"@llvm-project//llvm:BitWriter",
"@llvm-project//llvm:Core",
"@llvm-project//llvm:RISCVAsmParser",
Expand Down
2 changes: 2 additions & 0 deletions iree/compiler/Dialect/HAL/Target/LLVM/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ iree_cc_library(
LLVMAArch64CodeGen
LLVMARMAsmParser
LLVMARMCodeGen
LLVMBitReader
LLVMBitWriter
LLVMCore
LLVMRISCVAsmParser
Expand All @@ -50,6 +51,7 @@ iree_cc_library(
iree::compiler::Codegen::PassHeaders
iree::compiler::Codegen::Utils
iree::compiler::Dialect::HAL::Target
iree::compiler::Dialect::HAL::Target::LLVM::librt
iree::compiler::Utils
iree::schemas::dylib_executable_def_c_fbs
PUBLIC
Expand Down
100 changes: 90 additions & 10 deletions iree/compiler/Dialect/HAL/Target/LLVM/LLVMAOTTarget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@
#include "iree/compiler/Dialect/HAL/Target/LLVM/LibraryBuilder.h"
#include "iree/compiler/Dialect/HAL/Target/LLVM/LinkerTool.h"
#include "iree/compiler/Dialect/HAL/Target/LLVM/StaticLibraryGenerator.h"
#include "iree/compiler/Dialect/HAL/Target/LLVM/librt/librt.h"
#include "iree/compiler/Dialect/HAL/Target/TargetRegistry.h"
#include "iree/compiler/Utils/FlatbufferUtils.h"
#include "iree/schemas/dylib_executable_def_builder.h"
#include "llvm/Bitcode/BitcodeReader.h"
#include "llvm/Bitcode/BitcodeWriter.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
Expand All @@ -30,11 +32,10 @@ namespace iree_compiler {
namespace IREE {
namespace HAL {

namespace {
static constexpr char kQueryFunctionName[] =
"iree_hal_executable_library_query";

constexpr char kQueryFunctionName[] = "iree_hal_executable_library_query";

llvm::Optional<FileLineColLoc> findFirstFileLoc(Location baseLoc) {
static llvm::Optional<FileLineColLoc> findFirstFileLoc(Location baseLoc) {
if (auto loc = baseLoc.dyn_cast<FusedLoc>()) {
for (auto &childLoc : loc.getLocations()) {
auto childResult = findFirstFileLoc(childLoc);
Expand All @@ -46,7 +47,7 @@ llvm::Optional<FileLineColLoc> findFirstFileLoc(Location baseLoc) {
return llvm::None;
}

std::string guessModuleName(mlir::ModuleOp moduleOp) {
static std::string guessModuleName(mlir::ModuleOp moduleOp) {
std::string moduleName =
moduleOp.getName().hasValue() ? moduleOp.getName().getValue().str() : "";
if (!moduleName.empty()) return moduleName;
Expand All @@ -58,8 +59,6 @@ std::string guessModuleName(mlir::ModuleOp moduleOp) {
}
}

} // namespace

class LLVMAOTTargetBackend final : public TargetBackend {
public:
explicit LLVMAOTTargetBackend(LLVMTargetOptions options)
Expand Down Expand Up @@ -284,8 +283,10 @@ class LLVMAOTTargetBackend final : public TargetBackend {
<< options_.targetTriple << "'";
}

// Emit object files.
SmallVector<Artifact, 4> objectFiles;
SmallVector<Artifact> objectFiles;

// Emit the base object file containing the bulk of our code.
// This must come first such that we have the proper library linking order.
{
// NOTE: today we just use a single object file, however if we wanted to
// scale code generation and linking we'd want to generate one per
Expand All @@ -306,6 +307,16 @@ class LLVMAOTTargetBackend final : public TargetBackend {
objectFiles.push_back(std::move(objectFile));
}

// Optionally append additional object files that provide functionality that
// may otherwise have been runtime-dynamic (like libc/libm calls).
// For now we only do this for embedded uses.
if (options_.linkEmbedded) {
if (failed(buildLibraryObjects(variantOp.getLoc(), targetMachine.get(),
objectFiles, context))) {
return variantOp.emitError() << "failed generating library objects";
}
}

// If we are keeping artifacts then let's also add the bitcode for easier
// debugging (vs just the binary object file).
if (options_.keepLinkerArtifacts) {
Expand All @@ -329,7 +340,6 @@ class LLVMAOTTargetBackend final : public TargetBackend {
// Copy the static object file to the specified output along with
// generated header file.
const std::string &libraryPath = options_.staticLibraryOutput;
const auto library_name = objectFiles[0].path;
if (!outputStaticLibrary(libraryName, queryFunctionName, libraryPath,
objectFiles[0].path)) {
return variantOp.emitError() << "static library generation failed";
Expand Down Expand Up @@ -506,6 +516,76 @@ class LLVMAOTTargetBackend final : public TargetBackend {
return IREE::HAL::ExecutableTargetAttr::get(context, "llvm", format);
}

static void overridePlatformGlobal(llvm::Module &module, StringRef globalName,
uint32_t newValue) {
// NOTE: the global will not be defined if it is not used in the module.
auto *globalValue = module.getNamedGlobal(globalName);
if (!globalValue) return;
globalValue->setLinkage(llvm::GlobalValue::PrivateLinkage);
globalValue->setDSOLocal(true);
globalValue->setConstant(true);
globalValue->setInitializer(llvm::ConstantInt::get(
globalValue->getValueType(), APInt(32, newValue)));
}

// Builds an object file for the librt embedded runtime library.
// This is done per link operation so that we can match the precise target
// configuration. Since we (mostly) link once per user-level compilation
// this is fine today. If in the future we invoke the compiler for thousands
// of modules we'd want to (carefully) cache this.
LogicalResult buildLibraryObjects(Location loc,
llvm::TargetMachine *targetMachine,
SmallVector<Artifact> &objectFiles,
llvm::LLVMContext &context) {
assert(!objectFiles.empty() && "libraries must come after the base object");

// Load the generic bitcode file contents.
llvm::MemoryBufferRef bitcodeBufferRef(
llvm::StringRef(iree_compiler_librt_create()->data,
iree_compiler_librt_create()->size),
"librt.bc");
auto bitcodeModuleValue = llvm::parseBitcodeFile(bitcodeBufferRef, context);
if (!bitcodeModuleValue) {
return mlir::emitError(loc)
<< "failed to parse librt bitcode: "
<< llvm::toString(bitcodeModuleValue.takeError());
}
auto bitcodeModule = std::move(bitcodeModuleValue.get());
bitcodeModule->setDataLayout(targetMachine->createDataLayout());
bitcodeModule->setTargetTriple(targetMachine->getTargetTriple().str());

// Inject target-specific flags.
// TODO(benvanik): move this entire function to another file that can do
// more complex logic cleanly. This is just an example.
overridePlatformGlobal(*bitcodeModule, "librt_platform_example_flag", 0u);

// Run the LLVM passes to optimize it for the current target.
if (failed(runLLVMIRPasses(options_, targetMachine, bitcodeModule.get()))) {
return mlir::emitError(loc)
<< "failed to run librt LLVM-IR opt passes targeting '"
<< options_.targetTriple << "'";
}

// Emit an object file we can pass to the linker.
std::string objectData;
if (failed(runEmitObjFilePasses(targetMachine, bitcodeModule.get(),
&objectData))) {
return mlir::emitError(loc)
<< "failed to compile librt LLVM-IR module to an object file";
}

// Write the object file to disk with a similar name to the base file.
auto objectFile =
Artifact::createVariant(objectFiles.front().path, ".librt.o");
auto &os = objectFile.outputFile->os();
os << objectData;
os.flush();
os.close();
objectFiles.push_back(std::move(objectFile));

return success();
}

LLVMTargetOptions options_;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ class EmbeddedLinkerTool : public LinkerTool {

// Create the shared object name; if we only have a single input object we
// can just reuse that.
if (objectFiles.size() == 1) {
if (!objectFiles.empty()) {
artifacts.libraryFile =
Artifact::createVariant(objectFiles.front().path, "so");
} else {
Expand Down
31 changes: 31 additions & 0 deletions iree/compiler/Dialect/HAL/Target/LLVM/librt/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# 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

load("//build_tools/embed_data:build_defs.bzl", "c_embed_data")
load("//iree:build_defs.oss.bzl", "iree_cmake_extra_content")

package(
default_visibility = ["//visibility:public"],
features = ["layering_check"],
licenses = ["notice"], # Apache 2.0
)

iree_cmake_extra_content(
content = """
if(NOT "${IREE_TARGET_BACKEND_DYLIB-LLVM-AOT}" AND NOT "${IREE_TARGET_BACKEND_WASM-LLVM-AOT}")
return()
endif()
""",
)

c_embed_data(
name = "librt",
srcs = ["bin/librt.bc"],
c_file_output = "librt.c",
flatten = True,
h_file_output = "librt.h",
identifier = "iree_compiler_librt",
)
32 changes: 32 additions & 0 deletions iree/compiler/Dialect/HAL/Target/LLVM/librt/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
################################################################################
# Autogenerated by build_tools/bazel_to_cmake/bazel_to_cmake.py from #
# iree/compiler/Dialect/HAL/Target/LLVM/librt/BUILD #
# #
# Use iree_cmake_extra_content from iree/build_defs.oss.bzl to add arbitrary #
# CMake-only content. #
# #
# To disable autogeneration for this file entirely, delete this header. #
################################################################################

if(NOT "${IREE_TARGET_BACKEND_DYLIB-LLVM-AOT}" AND NOT "${IREE_TARGET_BACKEND_WASM-LLVM-AOT}")
return()
endif()

iree_add_all_subdirs()

iree_c_embed_data(
NAME
librt
SRCS
"bin/librt.bc"
C_FILE_OUTPUT
"librt.c"
H_FILE_OUTPUT
"librt.h"
IDENTIFIER
"iree_compiler_librt"
FLATTEN
PUBLIC
)

### BAZEL_TO_CMAKE_PRESERVES_ALL_CONTENT_BELOW_THIS_LINE ###
Binary file not shown.
45 changes: 45 additions & 0 deletions iree/compiler/Dialect/HAL/Target/LLVM/librt/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# 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

set -e

SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
OUT="${SCRIPT_DIR}/bin"
SRC="${SCRIPT_DIR}/src"
LL_FILE="${OUT}/librt.ll"
BC_FILE="${OUT}/librt.bc"

# Generate an LLVM IR assembly listing so we can easily read the file.
# This is not checked in or used by the compiler.
clang \
-target wasm32 \
-std=c17 \
-O2 \
-Xclang -disable-llvm-passes \
-fno-ident \
-fvisibility=hidden \
-nostdinc \
-g0 \
-S \
-emit-llvm \
-fno-verbose-asm \
-fdiscard-value-names \
-o "${LL_FILE}" \
-c \
"${SRC}/libm.c"

# Clang adds a bunch of bad attributes and host-specific information that we
# don't want (so we get at least somewhat deterministic builds).
sed -i 's/^;.*$//' ${LL_FILE}
sed -i 's/^source_filename.*$//' ${LL_FILE}
sed -i 's/^target datalayout.*$//' ${LL_FILE}
sed -i 's/^target triple.*$//' ${LL_FILE}
sed -i 's/^\(attributes #[0-9]* = {\).*$/\1 inlinehint }/' ${LL_FILE}

# Generate a binary bitcode file embedded into the compiler binary.
# NOTE: we do this from stdin so that the filename on the user's system is not
# embedded in the bitcode file (making it non-deterministic).
cat ${LL_FILE} | llvm-as -o=${BC_FILE}
13 changes: 13 additions & 0 deletions iree/compiler/Dialect/HAL/Target/LLVM/librt/src/libm.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// 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 "libm.h"

// https://en.cppreference.com/w/c/numeric/math/fma
LIBRT_EXPORT float fmaf(float x, float y, float z) {
// TODO(*): a real implementation :)
return (x * y) + z;
}
15 changes: 15 additions & 0 deletions iree/compiler/Dialect/HAL/Target/LLVM/librt/src/libm.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// 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

#ifndef IREE_COMPILER_DIALECT_HAL_TARGET_LLVM_LIBRT_SRC_LIBM_H_
#define IREE_COMPILER_DIALECT_HAL_TARGET_LLVM_LIBRT_SRC_LIBM_H_

#include "librt.h"

// https://en.cppreference.com/w/c/numeric/math/fma
LIBRT_EXPORT float fmaf(float x, float y, float z);

#endif // IREE_COMPILER_DIALECT_HAL_TARGET_LLVM_LIBRT_SRC_LIBM_H_
Loading

0 comments on commit 69afdd2

Please sign in to comment.