Skip to content

Commit 53b775a

Browse files
Support integrate 3rd-party toolchains into wamrc (#1237)
Support integrating 3rd-party toolchain llc compiler or asm compiler into wamrc by setting environment variable WAMRC_LLC_COMPILER or WAMRC_ASM_COMPILER, wamrc will use these tools to generate object file from LLVM IR firstly, and then refactor the object file into aot file.
1 parent bc6eda2 commit 53b775a

File tree

8 files changed

+241
-1
lines changed

8 files changed

+241
-1
lines changed

core/iwasm/compilation/aot.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
extern "C" {
1616
#endif
1717

18+
#ifndef AOT_FUNC_PREFIX
1819
#define AOT_FUNC_PREFIX "aot_func#"
20+
#endif
1921

2022
typedef InitializerExpression AOTInitExpr;
2123
typedef WASMType AOTFuncType;

core/iwasm/compilation/aot_compiler.c

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2886,6 +2886,36 @@ aot_compile_wasm(AOTCompContext *comp_ctx)
28862886
return true;
28872887
}
28882888

2889+
#if !(defined(_WIN32) || defined(_WIN32_))
2890+
char *
2891+
aot_generate_tempfile_name(const char *prefix, const char *extension,
2892+
char *buffer, uint32 len)
2893+
{
2894+
int fd, name_len;
2895+
2896+
name_len = snprintf(buffer, len, "%s-XXXXXX", prefix);
2897+
2898+
if ((fd = mkstemp(buffer)) <= 0) {
2899+
aot_set_last_error("make temp file failed.");
2900+
return NULL;
2901+
}
2902+
2903+
/* close and remove temp file */
2904+
close(fd);
2905+
unlink(buffer);
2906+
2907+
/* Check if buffer length is enough */
2908+
/* name_len + '.' + extension + '\0' */
2909+
if (name_len + 1 + strlen(extension) + 1 > len) {
2910+
aot_set_last_error("temp file name too long.");
2911+
return NULL;
2912+
}
2913+
2914+
snprintf(buffer + name_len, len - name_len, ".%s", extension);
2915+
return buffer;
2916+
}
2917+
#endif /* end of !(defined(_WIN32) || defined(_WIN32_)) */
2918+
28892919
#if WASM_ENABLE_LAZY_JIT == 0
28902920
bool
28912921
aot_emit_llvm_file(AOTCompContext *comp_ctx, const char *file_name)
@@ -2915,6 +2945,83 @@ aot_emit_object_file(AOTCompContext *comp_ctx, char *file_name)
29152945

29162946
bh_print_time("Begin to emit object file");
29172947

2948+
#if !(defined(_WIN32) || defined(_WIN32_))
2949+
if (comp_ctx->external_llc_compiler || comp_ctx->external_asm_compiler) {
2950+
char cmd[1024];
2951+
int ret;
2952+
2953+
if (comp_ctx->external_llc_compiler) {
2954+
char bc_file_name[64];
2955+
2956+
if (!aot_generate_tempfile_name("wamrc-bc", "bc", bc_file_name,
2957+
sizeof(bc_file_name))) {
2958+
return false;
2959+
}
2960+
2961+
if (LLVMWriteBitcodeToFile(comp_ctx->module, bc_file_name) != 0) {
2962+
aot_set_last_error("emit llvm bitcode file failed.");
2963+
return false;
2964+
}
2965+
2966+
snprintf(cmd, sizeof(cmd), "%s %s -o %s %s",
2967+
comp_ctx->external_llc_compiler,
2968+
comp_ctx->llc_compiler_flags ? comp_ctx->llc_compiler_flags
2969+
: "-O3 -c",
2970+
file_name, bc_file_name);
2971+
LOG_VERBOSE("invoking external LLC compiler:\n\t%s", cmd);
2972+
2973+
ret = system(cmd);
2974+
/* remove temp bitcode file */
2975+
unlink(bc_file_name);
2976+
2977+
if (ret != 0) {
2978+
aot_set_last_error("failed to compile LLVM bitcode to obj file "
2979+
"with external LLC compiler.");
2980+
return false;
2981+
}
2982+
}
2983+
else if (comp_ctx->external_asm_compiler) {
2984+
char asm_file_name[64];
2985+
2986+
if (!aot_generate_tempfile_name("wamrc-asm", "s", asm_file_name,
2987+
sizeof(asm_file_name))) {
2988+
return false;
2989+
}
2990+
2991+
if (LLVMTargetMachineEmitToFile(comp_ctx->target_machine,
2992+
comp_ctx->module, asm_file_name,
2993+
LLVMAssemblyFile, &err)
2994+
!= 0) {
2995+
if (err) {
2996+
LLVMDisposeMessage(err);
2997+
err = NULL;
2998+
}
2999+
aot_set_last_error("emit elf to assembly file failed.");
3000+
return false;
3001+
}
3002+
3003+
snprintf(cmd, sizeof(cmd), "%s %s -o %s %s",
3004+
comp_ctx->external_asm_compiler,
3005+
comp_ctx->asm_compiler_flags ? comp_ctx->asm_compiler_flags
3006+
: "-O3 -c",
3007+
file_name, asm_file_name);
3008+
LOG_VERBOSE("invoking external ASM compiler:\n\t%s", cmd);
3009+
3010+
ret = system(cmd);
3011+
/* remove temp assembly file */
3012+
unlink(asm_file_name);
3013+
3014+
if (ret != 0) {
3015+
aot_set_last_error("failed to compile Assembly file to obj "
3016+
"file with external ASM compiler.");
3017+
return false;
3018+
}
3019+
}
3020+
3021+
return true;
3022+
}
3023+
#endif /* end of !(defined(_WIN32) || defined(_WIN32_)) */
3024+
29183025
if (!strncmp(LLVMGetTargetName(target), "arc", 3))
29193026
/* Emit to assmelby file instead for arc target
29203027
as it cannot emit to object file */

core/iwasm/compilation/aot_compiler.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,10 @@ aot_emit_aot_file_buf(AOTCompContext *comp_ctx, AOTCompData *comp_data,
371371
bool
372372
aot_emit_object_file(AOTCompContext *comp_ctx, char *file_name);
373373

374+
char *
375+
aot_generate_tempfile_name(const char *prefix, const char *extension,
376+
char *buffer, uint32 len);
377+
374378
#ifdef __cplusplus
375379
} /* end of extern "C" */
376380
#endif

core/iwasm/compilation/aot_emit_aot_file.c

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2692,8 +2692,41 @@ aot_obj_data_create(AOTCompContext *comp_ctx)
26922692
memset(obj_data, 0, sizeof(AOTObjectData));
26932693

26942694
bh_print_time("Begin to emit object file");
2695+
if (comp_ctx->external_llc_compiler || comp_ctx->external_asm_compiler) {
2696+
#if defined(_WIN32) || defined(_WIN32_)
2697+
aot_set_last_error("external toolchain not supported on Windows");
2698+
goto fail;
2699+
#else
2700+
/* Generate a temp file name */
2701+
int ret;
2702+
char obj_file_name[64];
2703+
2704+
if (!aot_generate_tempfile_name("wamrc-obj", "o", obj_file_name,
2705+
sizeof(obj_file_name))) {
2706+
goto fail;
2707+
}
2708+
2709+
if (!aot_emit_object_file(comp_ctx, obj_file_name)) {
2710+
goto fail;
2711+
}
2712+
2713+
/* create memory buffer from object file */
2714+
ret = LLVMCreateMemoryBufferWithContentsOfFile(
2715+
obj_file_name, &obj_data->mem_buf, &err);
2716+
/* remove temp object file */
2717+
unlink(obj_file_name);
26952718

2696-
if (!strncmp(LLVMGetTargetName(target), "arc", 3)) {
2719+
if (ret != 0) {
2720+
if (err) {
2721+
LLVMDisposeMessage(err);
2722+
err = NULL;
2723+
}
2724+
aot_set_last_error("create mem buffer with file failed.");
2725+
goto fail;
2726+
}
2727+
#endif /* end of defined(_WIN32) || defined(_WIN32_) */
2728+
}
2729+
else if (!strncmp(LLVMGetTargetName(target), "arc", 3)) {
26972730
#if defined(_WIN32) || defined(_WIN32_)
26982731
aot_set_last_error("emit object file on Windows is unsupported.");
26992732
goto fail;

core/iwasm/compilation/aot_llvm.c

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1660,6 +1660,51 @@ aot_create_comp_context(AOTCompData *comp_data, aot_comp_option_t option)
16601660
opt_level = option->opt_level;
16611661
size_level = option->size_level;
16621662

1663+
/* verify external llc compiler */
1664+
comp_ctx->external_llc_compiler = getenv("WAMRC_LLC_COMPILER");
1665+
if (comp_ctx->external_llc_compiler) {
1666+
#if defined(_WIN32) || defined(_WIN32_)
1667+
comp_ctx->external_llc_compiler = NULL;
1668+
LOG_WARNING("External LLC compiler not supported on Windows.");
1669+
#else
1670+
if (access(comp_ctx->external_llc_compiler, X_OK) != 0) {
1671+
LOG_WARNING("WAMRC_LLC_COMPILER [%s] not found, fallback to "
1672+
"default pipeline",
1673+
comp_ctx->external_llc_compiler);
1674+
comp_ctx->external_llc_compiler = NULL;
1675+
}
1676+
else {
1677+
comp_ctx->llc_compiler_flags = getenv("WAMRC_LLC_FLAGS");
1678+
LOG_VERBOSE("Using external LLC compiler [%s]",
1679+
comp_ctx->external_llc_compiler);
1680+
}
1681+
#endif
1682+
}
1683+
1684+
/* verify external asm compiler */
1685+
if (!comp_ctx->external_llc_compiler) {
1686+
comp_ctx->external_asm_compiler = getenv("WAMRC_ASM_COMPILER");
1687+
if (comp_ctx->external_asm_compiler) {
1688+
#if defined(_WIN32) || defined(_WIN32_)
1689+
comp_ctx->external_asm_compiler = NULL;
1690+
LOG_WARNING("External ASM compiler not supported on Windows.");
1691+
#else
1692+
if (access(comp_ctx->external_asm_compiler, X_OK) != 0) {
1693+
LOG_WARNING(
1694+
"WAMRC_ASM_COMPILER [%s] not found, fallback to "
1695+
"default pipeline",
1696+
comp_ctx->external_asm_compiler);
1697+
comp_ctx->external_asm_compiler = NULL;
1698+
}
1699+
else {
1700+
comp_ctx->asm_compiler_flags = getenv("WAMRC_ASM_FLAGS");
1701+
LOG_VERBOSE("Using external ASM compiler [%s]",
1702+
comp_ctx->external_asm_compiler);
1703+
}
1704+
#endif
1705+
}
1706+
}
1707+
16631708
if (arch) {
16641709
/* Add default sub-arch if not specified */
16651710
if (!strcmp(arch, "arm"))

core/iwasm/compilation/aot_llvm.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "llvm-c/Object.h"
1515
#include "llvm-c/ExecutionEngine.h"
1616
#include "llvm-c/Analysis.h"
17+
#include "llvm-c/BitWriter.h"
1718
#include "llvm-c/Transforms/Utils.h"
1819
#include "llvm-c/Transforms/Scalar.h"
1920
#include "llvm-c/Transforms/Vectorize.h"
@@ -350,6 +351,20 @@ typedef struct AOTCompContext {
350351
uint32 func_ctx_count;
351352
char **custom_sections_wp;
352353
uint32 custom_sections_count;
354+
355+
/* 3rd-party toolchains */
356+
/* External llc compiler, if specified, wamrc will emit the llvm-ir file and
357+
* invoke the llc compiler to generate object file.
358+
* This can be used when we want to benefit from the optimization of other
359+
* LLVM based toolchains */
360+
const char *external_llc_compiler;
361+
const char *llc_compiler_flags;
362+
/* External asm compiler, if specified, wamrc will emit the text-based
363+
* assembly file (.s) and invoke the llc compiler to generate object file.
364+
* This will be useful when the upstream LLVM doesn't support to emit object
365+
* file for some architecture (such as arc) */
366+
const char *external_asm_compiler;
367+
const char *asm_compiler_flags;
353368
} AOTCompContext;
354369

355370
enum {

doc/build_wasm_app.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,36 @@ Examples: wamrc -o test.aot test.wasm
330330
wamrc --target=i386 --format=object -o test.o test.wasm
331331
```
332332
333+
## AoT compilation with 3rd-party toolchains
334+
335+
`wamrc` uses LLVM to compile wasm bytecode to AoT file, this works for most of the architectures, but there may be circumstances where you want to use 3rd-party toolchains to take over some steps of the compilation pipeline, e.g.
336+
337+
1. The upstream LLVM doesn't support generating object file for your CPU architecture (such as ARC), then we may need some other assembler to do such things.
338+
2. You may get some other LLVM-based toolchains which may have better optimizations for the specific target, then you may want your toolchain to take over all optimization steps.
339+
340+
`wamrc` provides two environment variables to achieve these:
341+
- `WAMRC_LLC_COMPILER`
342+
343+
When specified, `wamrc` will emit the optimized LLVM-IR (.bc) to a file, and invoke `$WAMRC_LLC_COMPILER` with ` -c -O3 ` to generate the object file.
344+
345+
Optionally, you can use environment variable `WAMRC_LLC_FLAGS` to overwrite the default flags.
346+
347+
- `WAMRC_ASM_COMPILER`
348+
349+
When specified, `wamrc` will emit the text based assembly file (.s), and invoke `$WAMRC_ASM_COMPILER` with ` -c -O3 ` to generate the object file.
350+
351+
Optionally, you can use environment variable `WAMRC_ASM_FLAGS` to overwrite the default flags.
352+
353+
### Usage example
354+
``` bash
355+
WAMRC_LLC_COMPILER=<path/to/your/compiler/driver> ./wamrc -o test.aot test.wasm
356+
```
357+
358+
> Note: `wamrc` will verify whether the specified file exists and executable. If verification failed, `wamrc` will report a warning and fallback to normal pipeline. Since the verification is based on file, you **must specify the absolute path to the binary** even if it's in `$PATH`
359+
360+
> Note: `WAMRC_LLC_COMPILER` has higher priority than `WAMRC_ASM_COMPILER`, if `WAMRC_LLC_COMPILER` is set and verified, then `WAMRC_ASM_COMPILER` will be ignored.
361+
362+
> Note: the `LLC` and `ASM` in the env name just means this compiler will be used to compile the `LLVM IR file`/`assembly file` to object file, usually passing the compiler driver is the simplest way. (e.g. for LLVM toolchain, you don't need to pass `/usr/bin/llc`, using `/usr/bin/clang` is OK)
333363

334364
Run WASM app in WAMR mini product build
335365
=======================================

wamr-compiler/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ if (WAMR_BUILD_LLVM_LEGACY_PM EQUAL 1)
4141
add_definitions(-DWASM_ENABLE_LLVM_LEGACY_PM=1)
4242
endif()
4343

44+
if (DEFINED WAMR_BUILD_AOT_FUNC_PREFIX)
45+
add_definitions(-DAOT_FUNC_PREFIX="${WAMR_BUILD_AOT_FUNC_PREFIX}")
46+
endif ()
47+
4448
# Set WAMR_BUILD_TARGET, currently values supported:
4549
# "X86_64", "AMD_64", "X86_32", "ARM_32", "MIPS_32", "XTENSA_32"
4650
if (NOT WAMR_BUILD_TARGET)

0 commit comments

Comments
 (0)