Skip to content

Commit bd1f59e

Browse files
committed
Merge remote-tracking branch 'origin/main' into set-perf
2 parents 21895c8 + a57f29e commit bd1f59e

File tree

19 files changed

+246
-175
lines changed

19 files changed

+246
-175
lines changed

BUILDING_FROM_SOURCE.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ To run the test suite (via `cargo test`), you additionally need to install:
7676
- [`valgrind`](https://www.valgrind.org/) (needs special treatment to [install on macOS](https://stackoverflow.com/a/61359781)
7777
Alternatively, you can use `cargo test --no-fail-fast` or `cargo test -p specific_tests` to skip over the valgrind failures & tests.
7878

79-
For debugging LLVM IR, we use [DebugIR](https://github.com/vaivaswatha/debugir). This dependency is only required to build with the `--debug` flag, and for normal development you should be fine without it.
79+
For emitting LLVM IR for debugging purposes, the `--emit-llvm-ir` flag can be used.
8080

8181
### libxcb libraries
8282

crates/cli/src/lib.rs

+29-10
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ pub const CMD_GLUE: &str = "glue";
5050
pub const CMD_GEN_STUB_LIB: &str = "gen-stub-lib";
5151
pub const CMD_PREPROCESS_HOST: &str = "preprocess-host";
5252

53-
pub const FLAG_DEBUG: &str = "debug";
53+
pub const FLAG_EMIT_LLVM_IR: &str = "emit-llvm-ir";
54+
pub const FLAG_PROFILING: &str = "profiling";
5455
pub const FLAG_BUNDLE: &str = "bundle";
5556
pub const FLAG_DEV: &str = "dev";
5657
pub const FLAG_OPTIMIZE: &str = "optimize";
@@ -102,9 +103,15 @@ pub fn build_app() -> Command {
102103
.action(ArgAction::SetTrue)
103104
.required(false);
104105

105-
let flag_debug = Arg::new(FLAG_DEBUG)
106-
.long(FLAG_DEBUG)
107-
.help("Store LLVM debug information in the generated program")
106+
let flag_emit_llvm_ir = Arg::new(FLAG_EMIT_LLVM_IR)
107+
.long(FLAG_EMIT_LLVM_IR)
108+
.help("Emit a `.ll` file containing the LLVM IR of the program")
109+
.action(ArgAction::SetTrue)
110+
.required(false);
111+
112+
let flag_profiling = Arg::new(FLAG_PROFILING)
113+
.long(FLAG_PROFILING)
114+
.help("Keep debug info in the final generated program even in optmized builds")
108115
.action(ArgAction::SetTrue)
109116
.required(false);
110117

@@ -163,7 +170,8 @@ pub fn build_app() -> Command {
163170
.arg(flag_max_threads.clone())
164171
.arg(flag_opt_size.clone())
165172
.arg(flag_dev.clone())
166-
.arg(flag_debug.clone())
173+
.arg(flag_emit_llvm_ir.clone())
174+
.arg(flag_profiling.clone())
167175
.arg(flag_time.clone())
168176
.arg(flag_linker.clone())
169177
.arg(flag_prebuilt.clone())
@@ -212,7 +220,8 @@ pub fn build_app() -> Command {
212220
.arg(flag_max_threads.clone())
213221
.arg(flag_opt_size.clone())
214222
.arg(flag_dev.clone())
215-
.arg(flag_debug.clone())
223+
.arg(flag_emit_llvm_ir.clone())
224+
.arg(flag_profiling.clone())
216225
.arg(flag_time.clone())
217226
.arg(flag_linker.clone())
218227
.arg(flag_prebuilt.clone())
@@ -234,7 +243,8 @@ pub fn build_app() -> Command {
234243
.arg(flag_max_threads.clone())
235244
.arg(flag_opt_size.clone())
236245
.arg(flag_dev.clone())
237-
.arg(flag_debug.clone())
246+
.arg(flag_emit_llvm_ir.clone())
247+
.arg(flag_profiling.clone())
238248
.arg(flag_time.clone())
239249
.arg(flag_linker.clone())
240250
.arg(flag_prebuilt.clone())
@@ -247,7 +257,8 @@ pub fn build_app() -> Command {
247257
.arg(flag_max_threads.clone())
248258
.arg(flag_opt_size.clone())
249259
.arg(flag_dev.clone())
250-
.arg(flag_debug.clone())
260+
.arg(flag_emit_llvm_ir.clone())
261+
.arg(flag_profiling.clone())
251262
.arg(flag_time.clone())
252263
.arg(flag_linker.clone())
253264
.arg(flag_prebuilt.clone())
@@ -376,7 +387,8 @@ pub fn build_app() -> Command {
376387
.arg(flag_max_threads)
377388
.arg(flag_opt_size)
378389
.arg(flag_dev)
379-
.arg(flag_debug)
390+
.arg(flag_emit_llvm_ir)
391+
.arg(flag_profiling)
380392
.arg(flag_time)
381393
.arg(flag_linker)
382394
.arg(flag_prebuilt)
@@ -696,7 +708,13 @@ pub fn build(
696708
CodeGenBackend::Llvm(backend_mode)
697709
};
698710

699-
let emit_debug_info = matches.get_flag(FLAG_DEBUG);
711+
let emit_llvm_ir = matches.get_flag(FLAG_EMIT_LLVM_IR);
712+
if emit_llvm_ir && !matches!(code_gen_backend, CodeGenBackend::Llvm(_)) {
713+
user_error!("Cannot emit llvm ir while using a dev backend.");
714+
}
715+
716+
let emit_debug_info = matches.get_flag(FLAG_PROFILING)
717+
|| matches!(opt_level, OptLevel::Development | OptLevel::Normal);
700718
let emit_timings = matches.get_flag(FLAG_TIME);
701719

702720
let threading = match matches.get_one::<usize>(FLAG_MAX_THREADS) {
@@ -745,6 +763,7 @@ pub fn build(
745763
backend: code_gen_backend,
746764
opt_level,
747765
emit_debug_info,
766+
emit_llvm_ir,
748767
};
749768

750769
let load_config = standard_load_config(&triple, build_ordering, threading);

crates/compiler/build/src/program.rs

+23-67
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ pub struct CodeGenOptions {
8585
pub backend: CodeGenBackend,
8686
pub opt_level: OptLevel,
8787
pub emit_debug_info: bool,
88+
pub emit_llvm_ir: bool,
8889
}
8990

9091
type GenFromMono<'a> = (CodeObject, CodeGenTiming, ExpectMetadata<'a>);
@@ -101,6 +102,7 @@ pub fn gen_from_mono_module<'a>(
101102
) -> GenFromMono<'a> {
102103
let path = roc_file_path;
103104
let debug = code_gen_options.emit_debug_info;
105+
let emit_llvm_ir = code_gen_options.emit_llvm_ir;
104106
let opt = code_gen_options.opt_level;
105107

106108
match code_gen_options.backend {
@@ -120,15 +122,23 @@ pub fn gen_from_mono_module<'a>(
120122
wasm_dev_stack_bytes,
121123
backend_mode,
122124
),
123-
CodeGenBackend::Llvm(backend_mode) => {
124-
gen_from_mono_module_llvm(arena, loaded, path, target, opt, backend_mode, debug)
125-
}
125+
CodeGenBackend::Llvm(backend_mode) => gen_from_mono_module_llvm(
126+
arena,
127+
loaded,
128+
path,
129+
target,
130+
opt,
131+
backend_mode,
132+
debug,
133+
emit_llvm_ir,
134+
),
126135
}
127136
}
128137

129138
// TODO how should imported modules factor into this? What if those use builtins too?
130139
// TODO this should probably use more helper functions
131140
// TODO make this polymorphic in the llvm functions so it can be reused for another backend.
141+
#[allow(clippy::too_many_arguments)]
132142
fn gen_from_mono_module_llvm<'a>(
133143
arena: &'a bumpalo::Bump,
134144
loaded: MonomorphizedModule<'a>,
@@ -137,6 +147,7 @@ fn gen_from_mono_module_llvm<'a>(
137147
opt_level: OptLevel,
138148
backend_mode: LlvmBackendMode,
139149
emit_debug_info: bool,
150+
emit_llvm_ir: bool,
140151
) -> GenFromMono<'a> {
141152
use crate::target::{self, convert_opt_level};
142153
use inkwell::attributes::{Attribute, AttributeLoc};
@@ -151,9 +162,6 @@ fn gen_from_mono_module_llvm<'a>(
151162
let context = Context::create();
152163
let module = arena.alloc(module_from_builtins(target, &context, "app"));
153164

154-
// strip Zig debug stuff
155-
// module.strip_debug_info();
156-
157165
// mark our zig-defined builtins as internal
158166
let app_ll_file = {
159167
let mut temp = PathBuf::from(roc_file_path);
@@ -245,8 +253,9 @@ fn gen_from_mono_module_llvm<'a>(
245253

246254
env.dibuilder.finalize();
247255

248-
// we don't use the debug info, and it causes weird errors.
249-
module.strip_debug_info();
256+
if !emit_debug_info {
257+
module.strip_debug_info();
258+
}
250259

251260
// Uncomment this to see the module's optimized LLVM instruction output:
252261
// env.module.print_to_stderr();
@@ -265,6 +274,11 @@ fn gen_from_mono_module_llvm<'a>(
265274
);
266275
}
267276

277+
if emit_llvm_ir {
278+
eprintln!("Emitting LLVM IR to {}", &app_ll_file.display());
279+
module.print_to_file(&app_ll_file).unwrap();
280+
}
281+
268282
// Uncomment this to see the module's optimized LLVM instruction output:
269283
// env.module.print_to_stderr();
270284

@@ -359,65 +373,6 @@ fn gen_from_mono_module_llvm<'a>(
359373

360374
assert!(bc_to_object.status.success(), "{bc_to_object:#?}");
361375

362-
MemoryBuffer::create_from_file(&app_o_file).expect("memory buffer creation works")
363-
} else if emit_debug_info {
364-
module.strip_debug_info();
365-
366-
let mut app_ll_dbg_file = PathBuf::from(roc_file_path);
367-
app_ll_dbg_file.set_extension("dbg.ll");
368-
369-
let mut app_o_file = PathBuf::from(roc_file_path);
370-
app_o_file.set_extension("o");
371-
372-
use std::process::Command;
373-
374-
// write the ll code to a file, so we can modify it
375-
module.print_to_file(&app_ll_file).unwrap();
376-
377-
// run the debugir https://github.com/vaivaswatha/debugir tool
378-
match Command::new("debugir")
379-
.args(["-instnamer", app_ll_file.to_str().unwrap()])
380-
.output()
381-
{
382-
Ok(_) => {}
383-
Err(error) => {
384-
use std::io::ErrorKind;
385-
match error.kind() {
386-
ErrorKind::NotFound => internal_error!(
387-
r"I could not find the `debugir` tool on the PATH, install it from https://github.com/vaivaswatha/debugir"
388-
),
389-
_ => internal_error!("{:?}", error),
390-
}
391-
}
392-
}
393-
394-
use target_lexicon::Architecture;
395-
match target.architecture {
396-
Architecture::X86_64
397-
| Architecture::X86_32(_)
398-
| Architecture::Aarch64(_)
399-
| Architecture::Wasm32 => {
400-
// write the .o file. Note that this builds the .o for the local machine,
401-
// and ignores the `target_machine` entirely.
402-
//
403-
// different systems name this executable differently, so we shotgun for
404-
// the most common ones and then give up.
405-
let ll_to_object = Command::new("llc")
406-
.args([
407-
"-relocation-model=pic",
408-
"-filetype=obj",
409-
app_ll_dbg_file.to_str().unwrap(),
410-
"-o",
411-
app_o_file.to_str().unwrap(),
412-
])
413-
.output()
414-
.unwrap();
415-
416-
assert!(ll_to_object.stderr.is_empty(), "{ll_to_object:#?}");
417-
}
418-
_ => unreachable!(),
419-
}
420-
421376
MemoryBuffer::create_from_file(&app_o_file).expect("memory buffer creation works")
422377
} else {
423378
// Emit the .o file
@@ -1326,6 +1281,7 @@ pub fn build_str_test<'a>(
13261281
backend: CodeGenBackend::Llvm(LlvmBackendMode::Binary),
13271282
opt_level: OptLevel::Normal,
13281283
emit_debug_info: false,
1284+
emit_llvm_ir: false,
13291285
};
13301286

13311287
let emit_timings = false;

crates/compiler/builtins/bitcode/build.zig

+3-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const CrossTarget = std.zig.CrossTarget;
77
const Arch = std.Target.Cpu.Arch;
88

99
pub fn build(b: *Build) void {
10-
// const mode = b.standardOptimizeOption(.{ .preferred_optimize_mode = .ReleaseFast });
10+
// const mode = b.standardOptimizeOption(.{ .preferred_optimize_mode = .Debug });
1111
const mode = b.standardOptimizeOption(.{ .preferred_optimize_mode = .ReleaseFast });
1212

1313
// Options
@@ -58,6 +58,7 @@ fn generateLlvmIrFile(
5858
) void {
5959
const obj = b.addObject(.{ .name = object_name, .root_source_file = main_path, .optimize = mode, .target = target, .use_llvm = true });
6060
obj.strip = true;
61+
obj.disable_stack_probing = true;
6162

6263
// Generating the bin seems required to get zig to generate the llvm ir.
6364
_ = obj.getEmittedBin();
@@ -89,6 +90,7 @@ fn generateObjectFile(
8990
obj.strip = true;
9091
obj.link_function_sections = true;
9192
obj.force_pic = true;
93+
obj.disable_stack_probing = true;
9294

9395
const obj_file = obj.getEmittedBin();
9496

0 commit comments

Comments
 (0)