Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changes to generate a custom GOT layout which can be safely reproduced #6

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
16 changes: 16 additions & 0 deletions compiler/plc_driver/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,18 @@ pub struct CompileParameters {
) ]
pub hardware_config: Option<String>,

#[clap(
name = "got-layout-file",
long,
global = true,
help = "Obtain information about the current custom GOT layout from the given file if it exists.
Save information about the generated custom GOT layout to the given file.
Format is detected by extension.
Supported formats : json, toml",
parse(try_from_str = validate_config)
) ]
pub got_layout_file: Option<String>,

#[clap(
name = "optimization",
long,
Expand Down Expand Up @@ -379,6 +391,10 @@ impl CompileParameters {
self.hardware_config.as_deref().and_then(get_config_format)
}

pub fn got_layout_format(&self) -> Option<ConfigFormat> {
self.got_layout_file.as_deref().and_then(get_config_format)
}

/// Returns the location where the build artifacts should be stored / output
pub fn get_build_location(&self) -> Option<PathBuf> {
match &self.commands {
Expand Down
10 changes: 8 additions & 2 deletions compiler/plc_driver/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ use std::{
use cli::{CompileParameters, ParameterError, SubCommands};
use pipelines::AnnotatedProject;
use plc::{
codegen::CodegenContext, linker::LinkerType, output::FormatOption, DebugLevel, ErrorFormat,
OptimizationLevel, Target, Threads,
codegen::CodegenContext, linker::LinkerType, output::FormatOption, ConfigFormat, DebugLevel,
ErrorFormat, OptimizationLevel, Target, Threads,
};

use plc_diagnostics::{diagnostician::Diagnostician, diagnostics::Diagnostic};
Expand Down Expand Up @@ -50,6 +50,8 @@ pub struct CompileOptions {
/// The name of the resulting compiled file
pub output: String,
pub output_format: FormatOption,
pub got_layout_file: Option<String>,
pub got_layout_format: Option<ConfigFormat>,
pub optimization: OptimizationLevel,
pub error_format: ErrorFormat,
pub debug_level: DebugLevel,
Expand All @@ -63,6 +65,8 @@ impl Default for CompileOptions {
build_location: None,
output: String::new(),
output_format: Default::default(),
got_layout_file: None,
got_layout_format: None,
optimization: OptimizationLevel::None,
error_format: ErrorFormat::None,
debug_level: DebugLevel::None,
Expand Down Expand Up @@ -172,6 +176,8 @@ pub fn get_compilation_context<T: AsRef<str> + AsRef<OsStr> + Debug>(
build_location: compile_parameters.get_build_location(),
output: project.get_output_name(),
output_format,
got_layout_file: compile_parameters.got_layout_file.clone(),
got_layout_format: compile_parameters.got_layout_format(),
optimization: compile_parameters.optimization,
error_format: compile_parameters.error_format,
debug_level: compile_parameters.debug_level(),
Expand Down
1 change: 1 addition & 0 deletions compiler/plc_driver/src/pipelines.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ impl<T: SourceContainer + Sync> AnnotatedProject<T> {
context,
compile_options.root.as_deref(),
&unit.file_name,
compile_options.got_layout_file.clone().zip(compile_options.got_layout_format),
compile_options.optimization,
compile_options.debug_level,
);
Expand Down
18 changes: 14 additions & 4 deletions src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use self::{
use crate::{
output::FormatOption,
resolver::{AstAnnotations, Dependency, StringLiterals},
DebugLevel, OptimizationLevel, Target,
ConfigFormat, DebugLevel, OptimizationLevel, Target,
};

use super::index::*;
Expand Down Expand Up @@ -71,6 +71,8 @@ pub struct CodeGen<'ink> {
/// the debugging module creates debug information at appropriate locations
pub debug: DebugBuilderEnum<'ink>,

pub got_layout_file: Option<(String, ConfigFormat)>,

pub module_location: String,
}

Expand All @@ -88,13 +90,14 @@ impl<'ink> CodeGen<'ink> {
context: &'ink CodegenContext,
root: Option<&Path>,
module_location: &str,
got_layout_file: Option<(String, ConfigFormat)>,
optimization_level: OptimizationLevel,
debug_level: DebugLevel,
) -> CodeGen<'ink> {
let module = context.create_module(module_location);
module.set_source_file_name(module_location);
let debug = debug::DebugBuilderEnum::new(context, &module, root, optimization_level, debug_level);
CodeGen { module, debug, module_location: module_location.to_string() }
CodeGen { module, debug, got_layout_file, module_location: module_location.to_string() }
}

pub fn generate_llvm_index(
Expand All @@ -117,8 +120,15 @@ impl<'ink> CodeGen<'ink> {
)?;
index.merge(llvm_type_index);

let mut variable_generator =
VariableGenerator::new(&self.module, &llvm, global_index, annotations, &index, &mut self.debug);
let mut variable_generator = VariableGenerator::new(
&self.module,
&llvm,
global_index,
annotations,
&index,
&mut self.debug,
self.got_layout_file.clone(),
);

//Generate global variables
let llvm_gv_index =
Expand Down
86 changes: 83 additions & 3 deletions src/codegen/generators/variable_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@ use crate::{
codegen::{debug::Debug, llvm_index::LlvmTypedIndex, llvm_typesystem::cast_if_needed},
index::{get_initializer_name, Index, PouIndexEntry, VariableIndexEntry},
resolver::{AnnotationMap, AstAnnotations, Dependency},
ConfigFormat,
};
use indexmap::IndexSet;
use inkwell::{module::Module, values::GlobalValue};
use inkwell::{module::Module, types::BasicTypeEnum, values::GlobalValue};
use plc_ast::ast::LinkageType;
use plc_diagnostics::diagnostics::Diagnostic;
use std::collections::HashMap;
use std::fs::{read_to_string, write};
use std::path::Path;

use super::{
data_type_generator::get_default_for,
Expand All @@ -18,13 +22,48 @@ use super::{
};
use crate::codegen::debug::DebugBuilderEnum;

pub fn read_got_layout(location: &str, format: ConfigFormat) -> Result<HashMap<String, u64>, Diagnostic> {
if !Path::new(location).is_file() {
// Assume if the file doesn't exist that there is no existing GOT layout yet. write_got_layout will handle
// creating our file when we want to.
return Ok(HashMap::new());
}

let s =
read_to_string(location).map_err(|_| Diagnostic::new("GOT layout could not be read from file"))?;
match format {
ConfigFormat::JSON => serde_json::from_str(&s)
.map_err(|_| Diagnostic::new("Could not deserialize GOT layout from JSON")),
ConfigFormat::TOML => {
toml::de::from_str(&s).map_err(|_| Diagnostic::new("Could not deserialize GOT layout from TOML"))
}
}
}

pub fn write_got_layout(
got_entries: HashMap<String, u64>,
location: &str,
format: ConfigFormat,
) -> Result<(), Diagnostic> {
let s = match format {
ConfigFormat::JSON => serde_json::to_string(&got_entries)
.map_err(|_| Diagnostic::new("Could not serialize GOT layout to JSON"))?,
ConfigFormat::TOML => toml::ser::to_string(&got_entries)
.map_err(|_| Diagnostic::new("Could not serialize GOT layout to TOML"))?,
};

write(location, s).map_err(|_| Diagnostic::new("GOT layout could not be written to file"))?;
Ok(())
}

pub struct VariableGenerator<'ctx, 'b> {
module: &'b Module<'ctx>,
llvm: &'b Llvm<'ctx>,
global_index: &'b Index,
annotations: &'b AstAnnotations,
types_index: &'b LlvmTypedIndex<'ctx>,
debug: &'b mut DebugBuilderEnum<'ctx>,
got_layout_file: Option<(String, ConfigFormat)>,
}

impl<'ctx, 'b> VariableGenerator<'ctx, 'b> {
Expand All @@ -35,8 +74,9 @@ impl<'ctx, 'b> VariableGenerator<'ctx, 'b> {
annotations: &'b AstAnnotations,
types_index: &'b LlvmTypedIndex<'ctx>,
debug: &'b mut DebugBuilderEnum<'ctx>,
got_layout_file: Option<(String, ConfigFormat)>,
) -> Self {
VariableGenerator { module, llvm, global_index, annotations, types_index, debug }
VariableGenerator { module, llvm, global_index, annotations, types_index, debug, got_layout_file }
}

pub fn generate_global_variables(
Expand Down Expand Up @@ -74,7 +114,7 @@ impl<'ctx, 'b> VariableGenerator<'ctx, 'b> {
}
});

for (name, variable) in globals {
for (name, variable) in &globals {
let linkage =
if !variable.is_in_unit(location) { LinkageType::External } else { variable.get_linkage() };
let global_variable = self.generate_global_variable(variable, linkage).map_err(|err| {
Expand All @@ -98,6 +138,46 @@ impl<'ctx, 'b> VariableGenerator<'ctx, 'b> {
);
}

if let Some((location, format)) = &self.got_layout_file {
let got_entries = read_got_layout(location.as_str(), *format)?;
let mut new_globals = Vec::new();
let mut new_got_entries = HashMap::new();
let mut new_got = HashMap::new();

for (name, _) in &globals {
if let Some(idx) = got_entries.get(&name.to_string()) {
new_got_entries.insert(name.to_string(), *idx);
new_got.insert(*idx, name.to_string());
} else {
new_globals.push(name.to_string());
}
}

// Put any globals that weren't there last time in any free space in the GOT.
let mut idx: u64 = 0;
for name in &new_globals {
while new_got.contains_key(&idx) {
idx += 1;
}
new_got_entries.insert(name.to_string(), idx);
new_got.insert(idx, name.to_string());
}

// Now we can write new_got_entries back out to a file.
write_got_layout(new_got_entries, location.as_str(), *format)?;

// Construct our GOT as a new global array. We initialise this array in the loader code.
let got_size = new_got.keys().max().map_or(0, |m| *m + 1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so we create an empty array here if the new GOT will be empty, but otherwise we reserve 1 + max slots? so the customGOT will be of size 0, or 2, 3, 4... etc? any reason for this? not criticizing, just want to make sure I understand everything :)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the map contains indices, it's 1 + max index. So if the maximum index is 0, we have 1 in the array so we should create an array of size 1. I suppose we could avoid creating the array at all if it's empty, but also it might be useful in other parts of the system to be able to assume that __custom_got exists.

let _got = self.llvm.create_global_variable(
self.module,
"__custom_got",
BasicTypeEnum::ArrayType(Llvm::get_array_type(
BasicTypeEnum::PointerType(self.llvm.context.i8_type().ptr_type(0.into())),
got_size.try_into().expect("the computed custom GOT size is too large"),
)),
);
}

Ok(index)
}

Expand Down