authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-07-23 15:58:18-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-07-23 16:04:58-07:00
log33d47424562672834ff758770b7ce89c637d4a1d
tree56808bda9fe5aea195facd3d415edfdf4ec56c07
parent7aaebd17746dc97111585563c64e166e27b334af

LLVM: more fine-grained sancov emit options

Exposes sanitizer coverage flags to the target machine emit function. Makes it easier to change sancov options without rebuilding the C++ files. This also enables PCTable = true for sancov which is needed by AFL, and adds the corresponding Clang flag.

5 files changed, 168 insertions(+), 95 deletions(-)

src/Compilation.zig+4
......@@ -5652,6 +5652,10 @@ pub fn addCCArgs(
56525652 // function was called.
56535653 try argv.append("-fno-sanitize=function");
56545654 }
5655
5656 if (mod.fuzz) {
5657 try argv.appendSlice(&.{ "-Xclang", "-fsanitize-coverage-trace-pc-guard" });
5658 }
56555659 }
56565660 }
56575661
src/codegen/llvm.zig+38-36
......@@ -1278,50 +1278,52 @@ pub const Object = struct {
12781278 // Unfortunately, LLVM shits the bed when we ask for both binary and assembly.
12791279 // So we call the entire pipeline multiple times if this is requested.
12801280 // var error_message: [*:0]const u8 = undefined;
1281 var emit_bin_path = options.bin_path;
1282 var post_ir_path = options.post_ir_path;
1281 var lowered_options: llvm.TargetMachine.EmitOptions = .{
1282 .is_debug = options.is_debug,
1283 .is_small = options.is_small,
1284 .time_report = options.time_report,
1285 .tsan = options.sanitize_thread,
1286 .sancov = options.fuzz,
1287 .lto = options.lto,
1288 .asm_filename = null,
1289 .bin_filename = options.bin_path,
1290 .llvm_ir_filename = options.post_ir_path,
1291 .bitcode_filename = null,
1292 .coverage = .{
1293 .CoverageType = .Edge,
1294 .IndirectCalls = true,
1295 .TraceBB = false,
1296 .TraceCmp = true,
1297 .TraceDiv = false,
1298 .TraceGep = false,
1299 .Use8bitCounters = false,
1300 .TracePC = false,
1301 .TracePCGuard = true,
1302 .Inline8bitCounters = true,
1303 .InlineBoolFlag = false,
1304 .PCTable = true,
1305 .NoPrune = false,
1306 .StackDepth = true,
1307 .TraceLoads = false,
1308 .TraceStores = false,
1309 .CollectControlFlow = false,
1310 },
1311 };
12831312 if (options.asm_path != null and options.bin_path != null) {
1284 if (target_machine.emitToFile(
1285 module,
1286 &error_message,
1287 options.is_debug,
1288 options.is_small,
1289 options.time_report,
1290 options.sanitize_thread,
1291 options.fuzz,
1292 options.lto,
1293 null,
1294 emit_bin_path,
1295 post_ir_path,
1296 null,
1297 )) {
1313 if (target_machine.emitToFile(module, &error_message, lowered_options)) {
12981314 defer llvm.disposeMessage(error_message);
1299
13001315 log.err("LLVM failed to emit bin={s} ir={s}: {s}", .{
13011316 emit_bin_msg, post_llvm_ir_msg, error_message,
13021317 });
13031318 return error.FailedToEmit;
13041319 }
1305 emit_bin_path = null;
1306 post_ir_path = null;
1307 }
1308
1309 if (target_machine.emitToFile(
1310 module,
1311 &error_message,
1312 options.is_debug,
1313 options.is_small,
1314 options.time_report,
1315 options.sanitize_thread,
1316 options.fuzz,
1317 options.lto,
1318 options.asm_path,
1319 emit_bin_path,
1320 post_ir_path,
1321 null,
1322 )) {
1323 defer llvm.disposeMessage(error_message);
1320 lowered_options.bin_filename = null;
1321 lowered_options.llvm_ir_filename = null;
1322 }
13241323
1324 lowered_options.asm_filename = options.asm_path;
1325 if (target_machine.emitToFile(module, &error_message, lowered_options)) {
1326 defer llvm.disposeMessage(error_message);
13251327 log.err("LLVM failed to emit asm={s} bin={s} ir={s} bc={s}: {s}", .{
13261328 emit_asm_msg, emit_bin_msg, post_llvm_ir_msg, post_llvm_bc_msg,
13271329 error_message,
src/codegen/llvm/bindings.zig+37-5
......@@ -84,11 +84,7 @@ pub const TargetMachine = opaque {
8484 pub const dispose = LLVMDisposeTargetMachine;
8585 extern fn LLVMDisposeTargetMachine(T: *TargetMachine) void;
8686
87 pub const emitToFile = ZigLLVMTargetMachineEmitToFile;
88 extern fn ZigLLVMTargetMachineEmitToFile(
89 T: *TargetMachine,
90 M: *Module,
91 ErrorMessage: *[*:0]const u8,
87 pub const EmitOptions = extern struct {
9288 is_debug: bool,
9389 is_small: bool,
9490 time_report: bool,
......@@ -99,6 +95,42 @@ pub const TargetMachine = opaque {
9995 bin_filename: ?[*:0]const u8,
10096 llvm_ir_filename: ?[*:0]const u8,
10197 bitcode_filename: ?[*:0]const u8,
98 coverage: Coverage,
99
100 pub const Coverage = extern struct {
101 CoverageType: Coverage.Type,
102 IndirectCalls: bool,
103 TraceBB: bool,
104 TraceCmp: bool,
105 TraceDiv: bool,
106 TraceGep: bool,
107 Use8bitCounters: bool,
108 TracePC: bool,
109 TracePCGuard: bool,
110 Inline8bitCounters: bool,
111 InlineBoolFlag: bool,
112 PCTable: bool,
113 NoPrune: bool,
114 StackDepth: bool,
115 TraceLoads: bool,
116 TraceStores: bool,
117 CollectControlFlow: bool,
118
119 pub const Type = enum(c_uint) {
120 None = 0,
121 Function,
122 BB,
123 Edge,
124 };
125 };
126 };
127
128 pub const emitToFile = ZigLLVMTargetMachineEmitToFile;
129 extern fn ZigLLVMTargetMachineEmitToFile(
130 T: *TargetMachine,
131 M: *Module,
132 ErrorMessage: *[*:0]const u8,
133 options: EmitOptions,
102134 ) bool;
103135
104136 pub const createTargetDataLayout = LLVMCreateTargetDataLayout;
src/zig_llvm.cpp+46-49
......@@ -189,59 +189,56 @@ struct TimeTracerRAII {
189189};
190190} // end anonymous namespace
191191
192static SanitizerCoverageOptions getSanCovOptions(void) {
192static SanitizerCoverageOptions getSanCovOptions(ZigLLVMCoverageOptions z) {
193193 SanitizerCoverageOptions o;
194 o.CoverageType = SanitizerCoverageOptions::SCK_Edge;
195 o.IndirectCalls = true;
196 o.TraceBB = false;
197 o.TraceCmp = true;
198 o.TraceDiv = false;
199 o.TraceGep = false;
200 o.Use8bitCounters = false;
201 o.TracePC = false;
202 o.TracePCGuard = false;
203 o.Inline8bitCounters = true;
204 o.InlineBoolFlag = false;
205 o.PCTable = true;
206 o.NoPrune = false;
207 o.StackDepth = true;
208 o.TraceLoads = false;
209 o.TraceStores = false;
210 o.CollectControlFlow = false;
194 o.CoverageType = (SanitizerCoverageOptions::Type)z.CoverageType;
195 o.IndirectCalls = z.IndirectCalls;
196 o.TraceBB = z.TraceBB;
197 o.TraceCmp = z.TraceCmp;
198 o.TraceDiv = z.TraceDiv;
199 o.TraceGep = z.TraceGep;
200 o.Use8bitCounters = z.Use8bitCounters;
201 o.TracePC = z.TracePC;
202 o.TracePCGuard = z.TracePCGuard;
203 o.Inline8bitCounters = z.Inline8bitCounters;
204 o.InlineBoolFlag = z.InlineBoolFlag;
205 o.PCTable = z.PCTable;
206 o.NoPrune = z.NoPrune;
207 o.StackDepth = z.StackDepth;
208 o.TraceLoads = z.TraceLoads;
209 o.TraceStores = z.TraceStores;
210 o.CollectControlFlow = z.CollectControlFlow;
211211 return o;
212212}
213213
214bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
215 char **error_message, bool is_debug,
216 bool is_small, bool time_report, bool tsan, bool sancov, bool lto,
217 const char *asm_filename, const char *bin_filename,
218 const char *llvm_ir_filename, const char *bitcode_filename)
214ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
215 char **error_message, struct ZigLLVMEmitOptions options)
219216{
220 TimePassesIsEnabled = time_report;
217 TimePassesIsEnabled = options.time_report;
221218
222219 raw_fd_ostream *dest_asm_ptr = nullptr;
223220 raw_fd_ostream *dest_bin_ptr = nullptr;
224221 raw_fd_ostream *dest_bitcode_ptr = nullptr;
225222
226 if (asm_filename) {
223 if (options.asm_filename) {
227224 std::error_code EC;
228 dest_asm_ptr = new(std::nothrow) raw_fd_ostream(asm_filename, EC, sys::fs::OF_None);
225 dest_asm_ptr = new(std::nothrow) raw_fd_ostream(options.asm_filename, EC, sys::fs::OF_None);
229226 if (EC) {
230227 *error_message = strdup((const char *)StringRef(EC.message()).bytes_begin());
231228 return true;
232229 }
233230 }
234 if (bin_filename) {
231 if (options.bin_filename) {
235232 std::error_code EC;
236 dest_bin_ptr = new(std::nothrow) raw_fd_ostream(bin_filename, EC, sys::fs::OF_None);
233 dest_bin_ptr = new(std::nothrow) raw_fd_ostream(options.bin_filename, EC, sys::fs::OF_None);
237234 if (EC) {
238235 *error_message = strdup((const char *)StringRef(EC.message()).bytes_begin());
239236 return true;
240237 }
241238 }
242 if (bitcode_filename) {
239 if (options.bitcode_filename) {
243240 std::error_code EC;
244 dest_bitcode_ptr = new(std::nothrow) raw_fd_ostream(bitcode_filename, EC, sys::fs::OF_None);
241 dest_bitcode_ptr = new(std::nothrow) raw_fd_ostream(options.bitcode_filename, EC, sys::fs::OF_None);
245242 if (EC) {
246243 *error_message = strdup((const char *)StringRef(EC.message()).bytes_begin());
247244 return true;
......@@ -257,7 +254,7 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM
257254 std::string ProcName = "zig-";
258255 ProcName += std::to_string(PID);
259256 TimeTracerRAII TimeTracer(ProcName,
260 bin_filename? bin_filename : asm_filename);
257 options.bin_filename? options.bin_filename : options.asm_filename);
261258
262259 TargetMachine &target_machine = *reinterpret_cast<TargetMachine*>(targ_machine_ref);
263260 target_machine.setO0WantsFastISel(true);
......@@ -266,11 +263,11 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM
266263
267264 // Pipeline configurations
268265 PipelineTuningOptions pipeline_opts;
269 pipeline_opts.LoopUnrolling = !is_debug;
270 pipeline_opts.SLPVectorization = !is_debug;
271 pipeline_opts.LoopVectorization = !is_debug;
272 pipeline_opts.LoopInterleaving = !is_debug;
273 pipeline_opts.MergeFunctions = !is_debug;
266 pipeline_opts.LoopUnrolling = !options.is_debug;
267 pipeline_opts.SLPVectorization = !options.is_debug;
268 pipeline_opts.LoopVectorization = !options.is_debug;
269 pipeline_opts.LoopInterleaving = !options.is_debug;
270 pipeline_opts.MergeFunctions = !options.is_debug;
274271
275272 // Instrumentations
276273 PassInstrumentationCallbacks instr_callbacks;
......@@ -308,19 +305,19 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM
308305 module_pm.addPass(VerifierPass());
309306 }
310307
311 if (!is_debug) {
308 if (!options.is_debug) {
312309 module_pm.addPass(createModuleToFunctionPassAdaptor(AddDiscriminatorsPass()));
313310 }
314311 });
315312
316313 pass_builder.registerOptimizerEarlyEPCallback([&](ModulePassManager &module_pm, OptimizationLevel OL) {
317314 // Code coverage instrumentation.
318 if (sancov) {
319 module_pm.addPass(SanitizerCoveragePass(getSanCovOptions()));
315 if (options.sancov) {
316 module_pm.addPass(SanitizerCoveragePass(getSanCovOptions(options.coverage)));
320317 }
321318
322319 // Thread sanitizer
323 if (tsan) {
320 if (options.tsan) {
324321 module_pm.addPass(ModuleThreadSanitizerPass());
325322 module_pm.addPass(createModuleToFunctionPassAdaptor(ThreadSanitizerPass()));
326323 }
......@@ -336,17 +333,17 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM
336333 ModulePassManager module_pm;
337334 OptimizationLevel opt_level;
338335 // Setting up the optimization level
339 if (is_debug)
336 if (options.is_debug)
340337 opt_level = OptimizationLevel::O0;
341 else if (is_small)
338 else if (options.is_small)
342339 opt_level = OptimizationLevel::Oz;
343340 else
344341 opt_level = OptimizationLevel::O3;
345342
346343 // Initialize the PassManager
347344 if (opt_level == OptimizationLevel::O0) {
348 module_pm = pass_builder.buildO0DefaultPipeline(opt_level, lto);
349 } else if (lto) {
345 module_pm = pass_builder.buildO0DefaultPipeline(opt_level, options.lto);
346 } else if (options.lto) {
350347 module_pm = pass_builder.buildLTOPreLinkDefaultPipeline(opt_level);
351348 } else {
352349 module_pm = pass_builder.buildPerModuleDefaultPipeline(opt_level);
......@@ -357,7 +354,7 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM
357354 codegen_pm.add(
358355 createTargetTransformInfoWrapperPass(target_machine.getTargetIRAnalysis()));
359356
360 if (dest_bin && !lto) {
357 if (dest_bin && !options.lto) {
361358 if (target_machine.addPassesToEmitFile(codegen_pm, *dest_bin, nullptr, CodeGenFileType::ObjectFile)) {
362359 *error_message = strdup("TargetMachine can't emit an object file");
363360 return true;
......@@ -376,20 +373,20 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM
376373 // Code generation phase
377374 codegen_pm.run(llvm_module);
378375
379 if (llvm_ir_filename) {
380 if (LLVMPrintModuleToFile(module_ref, llvm_ir_filename, error_message)) {
376 if (options.llvm_ir_filename) {
377 if (LLVMPrintModuleToFile(module_ref, options.llvm_ir_filename, error_message)) {
381378 return true;
382379 }
383380 }
384381
385 if (dest_bin && lto) {
382 if (dest_bin && options.lto) {
386383 WriteBitcodeToFile(llvm_module, *dest_bin);
387384 }
388385 if (dest_bitcode) {
389386 WriteBitcodeToFile(llvm_module, *dest_bitcode);
390387 }
391388
392 if (time_report) {
389 if (options.time_report) {
393390 TimerGroup::printAll(errs());
394391 }
395392
src/zig_llvm.h+43-5
......@@ -24,12 +24,50 @@
2424// ATTENTION: If you modify this file, be sure to update the corresponding
2525// extern function declarations in the self-hosted compiler.
2626
27ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
28 char **error_message, bool is_debug,
29 bool is_small, bool time_report, bool tsan, bool sancov, bool lto,
30 const char *asm_filename, const char *bin_filename,
31 const char *llvm_ir_filename, const char *bitcode_filename);
3227
28enum ZigLLVMCoverageType {
29 ZigLLVMCoverageType_None = 0,
30 ZigLLVMCoverageType_Function,
31 ZigLLVMCoverageType_BB,
32 ZigLLVMCoverageType_Edge
33};
34
35struct ZigLLVMCoverageOptions {
36 ZigLLVMCoverageType CoverageType;
37 bool IndirectCalls;
38 bool TraceBB;
39 bool TraceCmp;
40 bool TraceDiv;
41 bool TraceGep;
42 bool Use8bitCounters;
43 bool TracePC;
44 bool TracePCGuard;
45 bool Inline8bitCounters;
46 bool InlineBoolFlag;
47 bool PCTable;
48 bool NoPrune;
49 bool StackDepth;
50 bool TraceLoads;
51 bool TraceStores;
52 bool CollectControlFlow;
53};
54
55struct ZigLLVMEmitOptions {
56 bool is_debug;
57 bool is_small;
58 bool time_report;
59 bool tsan;
60 bool sancov;
61 bool lto;
62 const char *asm_filename;
63 const char *bin_filename;
64 const char *llvm_ir_filename;
65 const char *bitcode_filename;
66 ZigLLVMCoverageOptions coverage;
67};
68
69ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
70 char **error_message, struct ZigLLVMEmitOptions options);
3371
3472enum ZigLLVMABIType {
3573 ZigLLVMABITypeDefault, // Target-specific (either soft or hard depending on triple, etc).