Skip to content

Commit e048ff6

Browse files
hvanhovellmateiz
authored andcommitted
Make Runtime library statically linked (weld-project#229)
The current weld runtime library is dynamically loaded as a separate library. This is quite cumbersome when you use weld from java or python, such applications typically have less control over installing and loading native libraries. This PR tries to address this by statically linking the runtime library. This was not done before because the symbols needed by the generated code are removed during linking (because they appear not to be). This was especially problematic for the executables produced, the repl and the integration tests. This problem is fixed in two ways: - All the runtime functions (symbols) are exported in `runtime.rs` source file. This is needed to export the symbols in the library. This is very similar to approach take before weld-project@7a42598 was merged. - We add `-export_dynamic` to the link options, and we make sure that the static library only uses one object file. Both steps are needed to make integration tests and the repl working again. The downside to this approach is that the executables (not the libraries) almost double in size because all symbols are exported. In the future we could try to put executables on a diet by passing an explicit exported symbol list to the linker. I have also removed the `run_named` related functionality because this is not needed anymore. The PR has been tested on Mac and Linux.
1 parent 506a39d commit e048ff6

File tree

9 files changed

+130
-53
lines changed

9 files changed

+130
-53
lines changed

.cargo/config

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[target.x86_64-apple-darwin]
2+
rustflags = [ "-C", "link-args=-Wl,-export_dynamic" ]
3+
4+
[target.x86_64-unknown-linux-gnu]
5+
rustflags = [ "-C", "link-args=-Wl,-export-dynamic" ]

build.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,6 @@ fn main() {
4646
println!("cargo:rustc-link-lib=dylib=stdc++");
4747

4848
// Link the weldrt C++ library
49-
println!("cargo:rustc-link-lib=dylib=weldrt");
49+
println!("cargo:rustc-link-lib=static=weldrt");
5050
println!("cargo:rustc-link-search=native={}/weld_rt/cpp", project_dir);
5151
}

tests/integration_tests.rs

+38-1
Original file line numberDiff line numberDiff line change
@@ -2196,6 +2196,42 @@ fn predicate_if_iff_annotated() {
21962196
check_result_and_free(ret_value, expected);
21972197
}
21982198

2199+
fn nested_for_loops() {
2200+
#[derive(Clone)]
2201+
#[allow(dead_code)]
2202+
struct Row {
2203+
x: i64,
2204+
y: i32,
2205+
}
2206+
2207+
let code = "|ys:vec[i64]|result(for(ys, appender[{i64, i32}], |b0, i0, y0| for(ys, b0, |b1, i1, y1| if (y1 > y0, merge(b0, {y0, i32(y1)}), b0))))";
2208+
let conf = default_conf();
2209+
2210+
// Input data.
2211+
let ys = vec![1i64, 3i64, 4i64];
2212+
let ref input_data = WeldVec {
2213+
data: ys.as_ptr() as *const i64,
2214+
len: ys.len() as i64,
2215+
};
2216+
2217+
let ret_value = compile_and_run(code, conf, input_data);
2218+
let data = unsafe { weld_value_data(ret_value) as *const WeldVec<Row> };
2219+
let result = unsafe { (*data).clone() };
2220+
2221+
assert_eq!(result.len, 3i64);
2222+
let row = unsafe { (*result.data.offset(0)).clone() };
2223+
assert_eq!(row.x, 1i64);
2224+
assert_eq!(row.y, 3);
2225+
let row = unsafe { (*result.data.offset(1)).clone() };
2226+
assert_eq!(row.x, 1i64);
2227+
assert_eq!(row.y, 4);
2228+
let row = unsafe { (*result.data.offset(2)).clone() };
2229+
assert_eq!(row.x, 3i64);
2230+
assert_eq!(row.y, 4);
2231+
2232+
unsafe { free_value_and_module(ret_value) };
2233+
}
2234+
21992235
fn main() {
22002236
let args: Vec<String> = env::args().collect();
22012237
let tests: Vec<(&str, fn())> =
@@ -2278,7 +2314,8 @@ fn main() {
22782314
("outofmemory_error_test", outofmemory_error_test),
22792315
("simple_float_mod", simple_float_mod),
22802316
("simple_int_mod", simple_int_mod),
2281-
("predicate_if_iff_annotated", predicate_if_iff_annotated)];
2317+
("predicate_if_iff_annotated", predicate_if_iff_annotated),
2318+
("nested_for_loops", nested_for_loops)];
22822319

22832320
println!("");
22842321
println!("running tests");

weld/easy_ll/mod.rs

-6
Original file line numberDiff line numberDiff line change
@@ -79,12 +79,6 @@ impl CompiledModule {
7979
pub fn run(&self, arg: i64) -> i64 {
8080
(self.run_function.unwrap())(arg)
8181
}
82-
83-
/// Call a named function in the module. The function must take and return i64.
84-
pub fn run_named(&self, function_name: &str, arg: i64) -> Result<i64, LlvmError> {
85-
let func = unsafe { find_function(self.engine.unwrap(), function_name)? };
86-
Ok(func(arg))
87-
}
8882
}
8983

9084
impl Drop for CompiledModule {

weld/lib.rs

+3-15
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ pub mod parser;
4040
pub mod partial_types;
4141
pub mod pretty_print;
4242
pub mod program;
43+
pub mod runtime;
4344
pub mod sir;
4445
pub mod tokenizer;
4546
pub mod transforms;
@@ -197,8 +198,7 @@ pub unsafe extern "C" fn weld_value_free(obj: *mut WeldValue) {
197198
}
198199
let value = &mut *obj;
199200
if let Some(run_id) = value.run_id {
200-
let module = &mut *value.module.unwrap();
201-
module.run_named("run_dispose", run_id).unwrap();
201+
runtime::weld_run_dispose(run_id);
202202
}
203203
Box::from_raw(obj);
204204
}
@@ -213,8 +213,7 @@ pub unsafe extern "C" fn weld_value_memory_usage(obj: *mut WeldValue) -> libc::i
213213
}
214214
let value = &mut *obj;
215215
if let Some(run_id) = value.run_id {
216-
let module = &mut *value.module.unwrap();
217-
return module.run_named("run_memory_usage", run_id).unwrap_or(-1) as libc::int64_t;
216+
return runtime::weld_run_memory_usage(run_id);
218217
} else {
219218
return -1 as libc::int64_t;
220219
}
@@ -242,17 +241,6 @@ pub unsafe extern "C" fn weld_module_compile(code: *const c_char,
242241
let code = CStr::from_ptr(code);
243242
let code = code.to_str().unwrap().trim();
244243

245-
// Let LLVM find symbols in libweldrt; it's okay to call this multiple times
246-
let libweldrt = if cfg!(target_os="macos") {
247-
"libweldrt.dylib"
248-
} else if cfg!(target_os="windows") {
249-
"libweltrt.dll"
250-
} else {
251-
"libweldrt.so"
252-
};
253-
easy_ll::load_library(libweldrt).unwrap();
254-
info!("Loaded libweldrt in LLVM");
255-
256244
info!("Started parsing program");
257245
let parsed = parser::parse_program(code);
258246
info!("Done parsing program");

weld/llvm.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use super::macro_processor;
1717
use super::passes::*;
1818
use super::pretty_print::*;
1919
use super::program::Program;
20+
use super::runtime::*;
2021
use super::sir;
2122
use super::sir::*;
2223
use super::sir::Statement::*;
@@ -92,7 +93,9 @@ pub fn compile_program(program: &Program, opt_passes: &Vec<Pass>, llvm_opt_level
9293
debug!("Done compiling LLVM");
9394

9495
debug!("Started runtime_init call");
95-
module.run_named("runtime_init", 0).unwrap();
96+
unsafe {
97+
weld_runtime_init();
98+
}
9699
debug!("Done runtime_init call");
97100

98101
Ok(module)

weld/resources/prelude.ll

-18
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,13 @@ declare float @erff(float)
4343
declare double @erf(double)
4444

4545
; Weld runtime functions
46-
declare void @weld_runtime_init()
4746

4847
declare i64 @weld_run_begin(void (%work_t*)*, i8*, i64, i32)
4948
declare i8* @weld_run_get_result(i64)
50-
declare void @weld_run_dispose(i64)
5149

5250
declare i8* @weld_run_malloc(i64, i64)
5351
declare i8* @weld_run_realloc(i64, i8*, i64)
5452
declare void @weld_run_free(i64, i8*)
55-
declare i64 @weld_run_memory_usage(i64)
5653

5754
declare void @weld_run_set_errno(i64, i64)
5855
declare i64 @weld_run_get_errno(i64)
@@ -74,21 +71,6 @@ declare i8* @weld_rt_new_merger(i64, i32)
7471
declare i8* @weld_rt_get_merger_at_index(i8*, i64, i32)
7572
declare void @weld_rt_free_merger(i8*)
7673

77-
define i64 @run_dispose(i64 %run_id) {
78-
call void @weld_run_dispose(i64 %run_id)
79-
ret i64 0
80-
}
81-
82-
define i64 @runtime_init(i64 %unused) {
83-
call void @weld_runtime_init()
84-
ret i64 0
85-
}
86-
87-
define i64 @run_memory_usage(i64 %run_id) {
88-
%res = call i64 @weld_run_memory_usage(i64 %run_id)
89-
ret i64 %res
90-
}
91-
9274
; Parallel runtime structures
9375
; work_t struct in runtime.h
9476
%work_t = type { i8*, i64, i64, i64, i32, i64*, i64*, i32, i64, void (%work_t*)*, %work_t*, i32, i32, i32 }

weld/runtime.rs

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
use libc::{c_void, int64_t, int32_t, size_t};
2+
3+
#[allow(non_camel_case_types)]
4+
type work_t = c_void;
5+
6+
#[allow(non_camel_case_types)]
7+
type vec_piece = c_void;
8+
9+
#[repr(C)]
10+
pub struct vec_output {
11+
data: *mut c_void,
12+
size: i64,
13+
}
14+
15+
#[link(name="weldrt", kind="static")]
16+
extern "C" {
17+
#[no_mangle]
18+
pub fn weld_runtime_init();
19+
#[no_mangle]
20+
pub fn weld_rt_thread_id() -> int32_t;
21+
#[no_mangle]
22+
pub fn weld_rt_abort_thread();
23+
#[no_mangle]
24+
pub fn weld_rt_get_nworkers() -> int32_t;
25+
#[no_mangle]
26+
pub fn weld_rt_get_run_id() -> int64_t;
27+
#[no_mangle]
28+
pub fn weld_rt_start_loop(w: *mut work_t,
29+
body_data: *mut c_void,
30+
cont_data: *mut c_void,
31+
body: extern "C" fn(*mut work_t),
32+
cont: extern "C" fn(*mut work_t),
33+
lower: int64_t,
34+
upper: int64_t,
35+
grain_size: int32_t);
36+
#[no_mangle]
37+
pub fn weld_rt_set_result(res: *mut c_void);
38+
#[no_mangle]
39+
pub fn weld_rt_new_vb(elem_size: int64_t, starting_cap: int64_t, fixed_size: int32_t) -> *mut c_void;
40+
#[no_mangle]
41+
pub fn weld_rt_new_vb_piece(v: *mut c_void, w: *mut work_t);
42+
#[no_mangle]
43+
pub fn weld_rt_cur_vb_piece(v: *mut c_void, my_id: int32_t) -> *mut vec_piece;
44+
#[no_mangle]
45+
pub fn weld_rt_result_vb(v: *mut c_void) -> vec_output;
46+
#[no_mangle]
47+
pub fn weld_rt_new_merger(size: int64_t, nworkers: int32_t) -> *mut c_void;
48+
#[no_mangle]
49+
pub fn weld_rt_get_merger_at_index(m: *mut c_void, size: int64_t, i: int32_t) -> *mut c_void;
50+
#[no_mangle]
51+
pub fn weld_rt_free_merger(m: *mut c_void);
52+
#[no_mangle]
53+
pub fn weld_run_begin(run: extern "C" fn(*mut work_t), mem_limit: int64_t, n_workers: int32_t) -> int64_t;
54+
#[no_mangle]
55+
pub fn weld_run_get_result(run_id: int64_t) -> *mut c_void;
56+
#[no_mangle]
57+
pub fn weld_run_dispose(run_id: int64_t);
58+
#[no_mangle]
59+
pub fn weld_run_malloc(run_id: int64_t, size: size_t) -> *mut c_void;
60+
#[no_mangle]
61+
pub fn weld_run_realloc(run_id: int64_t, data: *mut c_void, size: size_t) -> *mut c_void;
62+
#[no_mangle]
63+
pub fn weld_run_free(run_id: int64_t, data: *mut c_void);
64+
#[no_mangle]
65+
pub fn weld_run_memory_usage(run_id: int64_t) -> int64_t;
66+
#[no_mangle]
67+
pub fn weld_run_get_errno(run_id: int64_t) -> int64_t;
68+
#[no_mangle]
69+
pub fn weld_run_set_errno(run_id: int64_t, err: int64_t);
70+
}

weld_rt/cpp/Makefile

+9-11
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,16 @@ CFLAGS = -O3 -std=c++11 -Wall -fno-use-cxa-atexit -fPIC
55
ifeq (${OS}, Darwin)
66
# OS X
77
CLANG ?= clang++-${LLVM_VERSION}
8-
DYLIB = libweldrt.dylib
8+
LIB = libweldrt.a
99
else ifeq (${OS}, Linux)
1010
# Linux
1111
CLANG ?= clang++-${LLVM_VERSION}
12-
DYLIB = libweldrt.so
12+
LIB = libweldrt.a
1313
else
1414
$(error Unsupported platform: ${OS})
1515
endif
1616

17-
all: ${DYLIB} inline.bc
17+
all: ${LIB} inline.bc
1818

1919
inline.o: inline.cpp
2020
${CLANG} ${CFLAGS} -c $< -o $@
@@ -31,15 +31,13 @@ merger.o: merger.cpp
3131
runtime.o: runtime.cpp
3232
${CLANG} ${CFLAGS} -c $< -o $@
3333

34-
${DYLIB}: vb.o merger.o runtime.o inline.o
35-
${CLANG} -shared -o $@ $^
36-
mkdir -p ../../target/release
37-
cp ${DYLIB} ../../target/release
38-
mkdir -p ../../target/debug
39-
cp ${DYLIB} ../../target/debug
34+
weldrt.o: vb.o merger.o runtime.o inline.o
35+
ld -r $^ -o $@
36+
37+
${LIB}: weldrt.o
38+
ar rcs $@ $^
4039

4140
clean:
42-
rm -f *.bc *.o ${DYLIB}
41+
rm -f *.bc *.o ${LIB}
4342

4443
.phony: all
45-

0 commit comments

Comments
 (0)