Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions compiler/plc_driver/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,14 @@ pub struct CompileParameters {
)]
pub gdwarf_varinfo_version: Option<usize>,

#[clap(
name = "gpubnames",
long,
help = "Enable .debug_names accelerator table emission (disabled by default for GDB compatibility)",
global = true
)]
pub gpubnames: bool,

#[clap(
name = "threads",
long,
Expand Down
2 changes: 2 additions & 0 deletions compiler/plc_driver/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ pub struct CompileOptions {
pub relocation_preference: RelocationPreference,
pub error_format: ErrorFormat,
pub debug_level: DebugLevel,
pub generate_pubnames: bool,
pub single_module: bool,
pub online_change: OnlineChange,
pub constructors_only: bool,
Expand All @@ -73,6 +74,7 @@ impl Default for CompileOptions {
relocation_preference: RelocationPreference::Default,
error_format: ErrorFormat::None,
debug_level: DebugLevel::None,
generate_pubnames: false,
single_module: false,
online_change: OnlineChange::Disabled,
constructors_only: false,
Expand Down
2 changes: 2 additions & 0 deletions compiler/plc_driver/src/pipelines.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ impl<T: SourceContainer> BuildPipeline<T> {
relocation_preference,
error_format: params.error_format,
debug_level: params.debug_level(),
generate_pubnames: params.gpubnames,
single_module: params.single_module,
online_change: if params.online_change {
OnlineChange::Enabled {
Expand Down Expand Up @@ -916,6 +917,7 @@ impl AnnotatedProject {
//FIXME don't clone here
compile_options.online_change.clone(),
target,
compile_options.generate_pubnames,
);
//Create a types codegen, this contains all the type declarations
//Associate the index type with LLVM types
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ declare void @mainProg(ptr)
!4 = !{}
!5 = !{i32 2, !"Dwarf Version", i32 5}
!6 = !{i32 2, !"Debug Info Version", i32 3}
!7 = distinct !DICompileUnit(language: DW_LANG_C, file: !8, producer: "RuSTy Structured text Compiler", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !9, splitDebugInlining: false)
!7 = distinct !DICompileUnit(language: DW_LANG_Pascal83, file: !8, producer: "RuSTy Structured text Compiler", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !9, splitDebugInlining: false, nameTableKind: None)
!8 = !DIFile(filename: "app/file1.st", directory: "root")
!9 = !{!0}
!10 = distinct !DISubprogram(name: "main", linkageName: "main", scope: !11, file: !11, line: 2, type: !12, scopeLine: 10, flags: DIFlagPublic, spFlags: DISPFlagDefinition, unit: !7, retainedNodes: !4)
Expand Down Expand Up @@ -71,7 +71,7 @@ entry:
!4 = !{}
!5 = !{i32 2, !"Dwarf Version", i32 5}
!6 = !{i32 2, !"Debug Info Version", i32 3}
!7 = distinct !DICompileUnit(language: DW_LANG_C, file: !8, producer: "RuSTy Structured text Compiler", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !9, splitDebugInlining: false)
!7 = distinct !DICompileUnit(language: DW_LANG_Pascal83, file: !8, producer: "RuSTy Structured text Compiler", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !9, splitDebugInlining: false, nameTableKind: None)
!8 = !DIFile(filename: "lib/file2.st", directory: "root")
!9 = !{!0}
!10 = distinct !DISubprogram(name: "mainProg", linkageName: "mainProg", scope: !2, file: !2, line: 2, type: !11, scopeLine: 5, flags: DIFlagPublic, spFlags: DISPFlagDefinition, unit: !7, retainedNodes: !4)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ declare void @mainProg(ptr)
!4 = !{}
!5 = !{i32 2, !"Dwarf Version", i32 5}
!6 = !{i32 2, !"Debug Info Version", i32 3}
!7 = distinct !DICompileUnit(language: DW_LANG_C, file: !8, producer: "RuSTy Structured text Compiler", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !9, splitDebugInlining: false)
!7 = distinct !DICompileUnit(language: DW_LANG_Pascal83, file: !8, producer: "RuSTy Structured text Compiler", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !9, splitDebugInlining: false, nameTableKind: None)
!8 = !DIFile(filename: "file1.st", directory: "root")
!9 = !{!0}
!10 = distinct !DISubprogram(name: "main", linkageName: "main", scope: !11, file: !11, line: 2, type: !12, scopeLine: 10, flags: DIFlagPublic, spFlags: DISPFlagDefinition, unit: !7, retainedNodes: !4)
Expand Down Expand Up @@ -71,7 +71,7 @@ entry:
!4 = !{}
!5 = !{i32 2, !"Dwarf Version", i32 5}
!6 = !{i32 2, !"Debug Info Version", i32 3}
!7 = distinct !DICompileUnit(language: DW_LANG_C, file: !8, producer: "RuSTy Structured text Compiler", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !9, splitDebugInlining: false)
!7 = distinct !DICompileUnit(language: DW_LANG_Pascal83, file: !8, producer: "RuSTy Structured text Compiler", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !9, splitDebugInlining: false, nameTableKind: None)
!8 = !DIFile(filename: "file2.st", directory: "root")
!9 = !{!0}
!10 = distinct !DISubprogram(name: "mainProg", linkageName: "mainProg", scope: !2, file: !2, line: 2, type: !11, scopeLine: 5, flags: DIFlagPublic, spFlags: DISPFlagDefinition, unit: !7, retainedNodes: !4)
Expand Down
61 changes: 61 additions & 0 deletions compiler/plc_llvm/src/cpp/llvm_wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
#include <llvm/Target/TargetMachine.h>
#include <llvm-c/TargetMachine.h>
#include <llvm/Support/CBindingWrapping.h>
#include <llvm/IR/DIBuilder.h>
#include <llvm/IR/DebugInfoMetadata.h>
#include <llvm-c/DebugInfo.h>

using namespace llvm;

Expand All @@ -17,4 +20,62 @@ extern "C" {
TargetOptions* options = &targetMachine->Options;
options->UseInitArray = useInitArray ? 1 : 0;
}

// Create a DICompileUnit via the C++ DIBuilder API, which exposes the
// NameTableKind parameter that the LLVM C API does not.
// When enablePubnames is false, sets NameTableKind::None to suppress
// .debug_names emission (avoiding a GDB incompatibility with lld).
//
// The `lang` and `emissionKind` parameters use the LLVMDWARFSourceLanguage
// and LLVMDWARFEmissionKind C enum values (0-based sequential), which need
// to be mapped to their DWARF/C++ equivalents.
LLVMMetadataRef createCompileUnit(
LLVMDIBuilderRef builderRef,
LLVMMetadataRef fileRef,
unsigned lang,
const char *producer, size_t producerLen,
LLVMBool isOptimized,
unsigned runtimeVer,
unsigned emissionKind,
uint64_t dwoId,
LLVMBool splitDebugInlining,
LLVMBool debugInfoForProfiling,
LLVMBool enablePubnames,
const char *sysRoot, size_t sysRootLen,
const char *sdk, size_t sdkLen
) {
auto *builder = unwrap(builderRef);
auto *file = cast<DIFile>(unwrap(fileRef));
auto nameTableKind = enablePubnames
? DICompileUnit::DebugNameTableKind::Default
: DICompileUnit::DebugNameTableKind::None;

// The LLVM C API uses a 0-based sequential enum for source languages
// (LLVMDWARFSourceLanguageC89=0, C=1, ...) while the C++ API / DWARF
// spec uses the actual DWARF values (DW_LANG_C89=0x0001, C=0x0002, ...).
// Add 1 to convert from the C enum to the DWARF value.
unsigned dwarfLang = lang + 1;

// Similarly, LLVMDWARFEmissionKind: None=0, Full=1, LineTablesOnly=2
// maps to DICompileUnit::DebugEmissionKind where NoDebug=0, FullDebug=1,
// LineTablesOnly=2, DebugDirectivesOnly=3 — these happen to match directly.

auto *cu = builder->createCompileUnit(
dwarfLang, file,
StringRef(producer, producerLen),
isOptimized,
/*Flags=*/"",
runtimeVer,
/*SplitName=*/"",
static_cast<DICompileUnit::DebugEmissionKind>(emissionKind),
dwoId,
splitDebugInlining,
debugInfoForProfiling,
nameTableKind,
/*RangesBaseAddress=*/false,
StringRef(sysRoot, sysRootLen),
StringRef(sdk, sdkLen)
);
return wrap(cu);
}
}
130 changes: 128 additions & 2 deletions compiler/plc_llvm/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,50 @@
//! This crate provides Rust bindings for LLVM Target Machine functionalities.
//! This crate provides Rust bindings for LLVM Target Machine functionalities
//! and extensions to inkwell's debug info API.

use std::ffi::c_void;
use std::marker::PhantomData;

use inkwell::debug_info::{DICompileUnit, DIFile, DWARFEmissionKind, DWARFSourceLanguage, DebugInfoBuilder};
use inkwell::llvm_sys::debuginfo::{LLVMDWARFEmissionKind, LLVMDWARFSourceLanguage};
use inkwell::llvm_sys::prelude::LLVMBool;
use inkwell::module::Module;
use inkwell::targets::TargetMachine;

/// Opaque pointer matching LLVMDIBuilderRef (= *mut LLVMOpaqueDIBuilder).
/// We define our own alias to avoid depending on the private re-export path.
type LLVMDIBuilderRef = *mut c_void;

mod ffi {
use inkwell::llvm_sys::prelude::LLVMBool;
use std::ffi::c_void;

use inkwell::llvm_sys::prelude::{LLVMBool, LLVMMetadataRef};
use inkwell::llvm_sys::target_machine::LLVMTargetMachineRef;

/// Opaque pointer matching LLVMDIBuilderRef.
type LLVMDIBuilderRef = *mut c_void;

#[link(name = "llvm_wrapper")]
unsafe extern "C" {
pub fn setUseInitArray(tm: LLVMTargetMachineRef, use_init_array: LLVMBool);

pub fn createCompileUnit(
builder: LLVMDIBuilderRef,
file: LLVMMetadataRef,
lang: std::ffi::c_uint,
producer: *const std::ffi::c_char,
producer_len: usize,
is_optimized: LLVMBool,
runtime_ver: std::ffi::c_uint,
emission_kind: std::ffi::c_uint,
dwo_id: u64,
split_debug_inlining: LLVMBool,
debug_info_for_profiling: LLVMBool,
enable_pubnames: LLVMBool,
sys_root: *const std::ffi::c_char,
sys_root_len: usize,
sdk: *const std::ffi::c_char,
sdk_len: usize,
) -> LLVMMetadataRef;
}
}

Expand All @@ -31,3 +66,94 @@ impl TargetMachineExt for TargetMachine {
}
}
}

/// Creates a [`DebugInfoBuilder`] and [`DICompileUnit`] with control over `.debug_names` emission.
///
/// This wraps LLVM's C++ `DIBuilder::createCompileUnit` which exposes the `NameTableKind`
/// parameter that the LLVM C API does not. When `generate_pubnames` is `false`, sets
/// `NameTableKind::None` to suppress `.debug_names` emission, avoiding a GDB incompatibility
/// when multiple compilation units are linked with `lld`.
#[allow(clippy::too_many_arguments)]
pub fn create_debug_info<'ctx>(
module: &Module<'ctx>,
allow_unresolved: bool,
language: DWARFSourceLanguage,
filename: &str,
directory: &str,
producer: &str,
is_optimized: bool,
runtime_ver: std::ffi::c_uint,
emission_kind: DWARFEmissionKind,
generate_pubnames: bool,
) -> (DebugInfoBuilder<'ctx>, DICompileUnit<'ctx>) {
use inkwell::llvm_sys::debuginfo::{
LLVMCreateDIBuilder, LLVMCreateDIBuilderDisallowUnresolved, LLVMDIBuilderCreateFile,
};

unsafe {
let raw_module = module.as_mut_ptr();
let builder_ref: LLVMDIBuilderRef = if allow_unresolved {
LLVMCreateDIBuilder(raw_module).cast()
} else {
LLVMCreateDIBuilderDisallowUnresolved(raw_module).cast()
};

let file_ref = LLVMDIBuilderCreateFile(
builder_ref.cast(),
filename.as_ptr().cast(),
filename.len(),
directory.as_ptr().cast(),
directory.len(),
);

let cu_ref = ffi::createCompileUnit(
builder_ref,
file_ref,
{
let lang: LLVMDWARFSourceLanguage = language.into();
lang as std::ffi::c_uint
},
producer.as_ptr().cast(),
producer.len(),
is_optimized as LLVMBool,
runtime_ver,
{
let kind: LLVMDWARFEmissionKind = emission_kind.into();
kind as std::ffi::c_uint
},
0, // dwo_id
0, // split_debug_inlining = false
0, // debug_info_for_profiling = false
generate_pubnames as LLVMBool,
c"".as_ptr(),
0,
c"".as_ptr(),
0,
);

// Construct inkwell wrapper types from raw LLVM pointers.
// These types are thin wrappers with predictable layout:
// DebugInfoBuilder { builder: LLVMDIBuilderRef, _marker: PhantomData }
// DIFile { metadata_ref: LLVMMetadataRef, _marker: PhantomData }
// DICompileUnit { file: DIFile, metadata_ref: LLVMMetadataRef, _marker: PhantomData }
let debug_info: DebugInfoBuilder<'ctx> = std::mem::transmute(builder_ref);
let compile_unit: DICompileUnit<'ctx> =
std::mem::transmute((file_ref, cu_ref, PhantomData::<&'ctx ()>));

// Compile-time layout verification
const _: () = {
assert!(size_of::<DebugInfoBuilder>() == size_of::<usize>() + size_of::<PhantomData<&()>>());
assert!(
size_of::<DICompileUnit>()
== size_of::<DIFile>() + size_of::<usize>() + size_of::<PhantomData<&()>>()
);
};

// Runtime verification in debug builds
debug_assert_eq!(debug_info.as_mut_ptr().cast::<c_void>(), builder_ref);
debug_assert_eq!(compile_unit.as_mut_ptr(), cu_ref);
debug_assert_eq!(compile_unit.get_file().as_mut_ptr(), file_ref);

(debug_info, compile_unit)
}
}
11 changes: 10 additions & 1 deletion src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ type MainEmptyFunction<U> = unsafe extern "C" fn() -> U;

impl<'ink> CodeGen<'ink> {
/// constructs a new code-generator that generates CompilationUnits into a module with the given module_name
#[allow(clippy::too_many_arguments)]
pub fn new(
context: &'ink CodegenContext,
root: Option<&Path>,
Expand All @@ -104,6 +105,7 @@ impl<'ink> CodeGen<'ink> {
debug_level: DebugLevel,
online_change: OnlineChange,
target: &Target,
generate_pubnames: bool,
) -> CodeGen<'ink> {
let module_location = file_marker.get_name().unwrap_or_default();
let module = context.create_module(module_location);
Expand Down Expand Up @@ -136,7 +138,14 @@ impl<'ink> CodeGen<'ink> {
module.set_triple(&triple);

let debug_level = if file_marker.is_internal() { DebugLevel::None } else { debug_level };
let debug = debug::DebugBuilderEnum::new(context, &module, root, optimization_level, debug_level);
let debug = debug::DebugBuilderEnum::new(
context,
&module,
root,
optimization_level,
debug_level,
generate_pubnames,
);
CodeGen { module, debug, module_location: module_location.to_string(), online_change }
}

Expand Down
14 changes: 5 additions & 9 deletions src/codegen/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ impl<'ink> DebugBuilderEnum<'ink> {
root: Option<&Path>,
optimization: OptimizationLevel,
debug_level: DebugLevel,
generate_pubnames: bool,
) -> Self {
match debug_level {
DebugLevel::None => DebugBuilderEnum::None,
Expand Down Expand Up @@ -229,22 +230,17 @@ impl<'ink> DebugBuilderEnum<'ink> {
let path = Path::new(module.get_source_file_name().to_str().unwrap_or("")).to_path_buf();
let root = root.unwrap_or_else(|| Path::new(""));
let filename = path.strip_prefix(root).unwrap_or(&path).to_str().unwrap_or_default();
let (debug_info, compile_unit) = module.create_debug_info_builder(
let (debug_info, compile_unit) = plc_llvm::create_debug_info(
module,
true,
inkwell::debug_info::DWARFSourceLanguage::C, //TODO: Own lang
inkwell::debug_info::DWARFSourceLanguage::Pascal83,
filename,
root.to_str().unwrap_or_default(),
"RuSTy Structured text Compiler",
optimization.is_optimized(),
"",
0,
"",
debug_level.into(),
0,
false,
false,
"",
"",
generate_pubnames,
);

let data_layout = module.get_data_layout();
Expand Down
Loading
Loading