Skip to content

Commit 1ad2fc9

Browse files
lewis-revillCohenArthur
authored andcommitted
feat: Generate a custom GOT array and save/load its layout
This commit contains changes that are required to generate a global variable which will be used to store an array of addresses of other global variables in the program. The location of the globals within this array should be stable after a recompilation to support online change, so we support loading a pre-existing layout to ensure that for all globals that we see in both the current program and the pre-existing layout, their positions remain the same. Otherwise, any new globals will be placed in any empty spaces left by old globals, or appended on to the end of the array. The layout will then be saved back the the file used for saving and loading. Currently, to use this feature the flag `--got-layout-file=<file>` must be provided, which should specify the name of either a TOML or JSON file to use to save, and optionally load if the file already exists, the GOT layout. In future we will integrate this with a generic online change flag, whereby it will not be necessary to ask for a GOT layout file when we already know that we need it for online change.
1 parent 50dc477 commit 1ad2fc9

File tree

5 files changed

+123
-10
lines changed

5 files changed

+123
-10
lines changed

compiler/plc_driver/src/cli.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,18 @@ pub struct CompileParameters {
108108
) ]
109109
pub hardware_config: Option<String>,
110110

111+
#[clap(
112+
name = "got-layout-file",
113+
long,
114+
global = true,
115+
help = "Obtain information about the current custom GOT layout from the given file if it exists.
116+
Save information about the generated custom GOT layout to the given file.
117+
Format is detected by extension.
118+
Supported formats : json, toml",
119+
parse(try_from_str = validate_config)
120+
) ]
121+
pub got_layout_file: Option<String>,
122+
111123
#[clap(
112124
name = "optimization",
113125
long,
@@ -387,6 +399,10 @@ impl CompileParameters {
387399
self.hardware_config.as_deref().and_then(get_config_format)
388400
}
389401

402+
pub fn got_layout_format(&self) -> Option<ConfigFormat> {
403+
self.got_layout_file.as_deref().and_then(get_config_format)
404+
}
405+
390406
/// Returns the location where the build artifacts should be stored / output
391407
pub fn get_build_location(&self) -> Option<PathBuf> {
392408
match &self.commands {

compiler/plc_driver/src/lib.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ use std::{
1919
use cli::{CompileParameters, ParameterError, SubCommands};
2020
use pipelines::AnnotatedProject;
2121
use plc::{
22-
codegen::CodegenContext, linker::LinkerType, output::FormatOption, DebugLevel, ErrorFormat,
23-
OptimizationLevel, Target, Threads,
22+
codegen::CodegenContext, linker::LinkerType, output::FormatOption, ConfigFormat, DebugLevel,
23+
ErrorFormat, OptimizationLevel, Target, Threads,
2424
};
2525

2626
use plc_diagnostics::{diagnostician::Diagnostician, diagnostics::Diagnostic};
@@ -50,6 +50,8 @@ pub struct CompileOptions {
5050
/// The name of the resulting compiled file
5151
pub output: String,
5252
pub output_format: FormatOption,
53+
pub got_layout_file: Option<String>,
54+
pub got_layout_format: Option<ConfigFormat>,
5355
pub optimization: OptimizationLevel,
5456
pub error_format: ErrorFormat,
5557
pub debug_level: DebugLevel,
@@ -63,6 +65,8 @@ impl Default for CompileOptions {
6365
build_location: None,
6466
output: String::new(),
6567
output_format: Default::default(),
68+
got_layout_file: None,
69+
got_layout_format: None,
6670
optimization: OptimizationLevel::None,
6771
error_format: ErrorFormat::None,
6872
debug_level: DebugLevel::None,
@@ -172,6 +176,8 @@ pub fn get_compilation_context<T: AsRef<str> + AsRef<OsStr> + Debug>(
172176
build_location: compile_parameters.get_build_location(),
173177
output: project.get_output_name(),
174178
output_format,
179+
got_layout_file: compile_parameters.got_layout_file.clone(),
180+
got_layout_format: compile_parameters.got_layout_format(),
175181
optimization: compile_parameters.optimization,
176182
error_format: compile_parameters.error_format,
177183
debug_level: compile_parameters.debug_level(),

compiler/plc_driver/src/pipelines.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@ impl<T: SourceContainer + Sync> AnnotatedProject<T> {
274274
context,
275275
compile_options.root.as_deref(),
276276
&unit.file_name,
277+
compile_options.got_layout_file.clone().zip(compile_options.got_layout_format),
277278
compile_options.optimization,
278279
compile_options.debug_level,
279280
);

src/codegen.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use self::{
1919
use crate::{
2020
output::FormatOption,
2121
resolver::{AstAnnotations, Dependency, StringLiterals},
22-
DebugLevel, OptimizationLevel, Target,
22+
ConfigFormat, DebugLevel, OptimizationLevel, Target,
2323
};
2424

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

74+
pub got_layout_file: Option<(String, ConfigFormat)>,
75+
7476
pub module_location: String,
7577
}
7678

@@ -88,13 +90,14 @@ impl<'ink> CodeGen<'ink> {
8890
context: &'ink CodegenContext,
8991
root: Option<&Path>,
9092
module_location: &str,
93+
got_layout_file: Option<(String, ConfigFormat)>,
9194
optimization_level: OptimizationLevel,
9295
debug_level: DebugLevel,
9396
) -> CodeGen<'ink> {
9497
let module = context.create_module(module_location);
9598
module.set_source_file_name(module_location);
9699
let debug = debug::DebugBuilderEnum::new(context, &module, root, optimization_level, debug_level);
97-
CodeGen { module, debug, module_location: module_location.to_string() }
100+
CodeGen { module, debug, got_layout_file, module_location: module_location.to_string() }
98101
}
99102

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

120-
let mut variable_generator =
121-
VariableGenerator::new(&self.module, &llvm, global_index, annotations, &index, &mut self.debug);
123+
let mut variable_generator = VariableGenerator::new(
124+
&self.module,
125+
&llvm,
126+
global_index,
127+
annotations,
128+
&index,
129+
&mut self.debug,
130+
self.got_layout_file.clone(),
131+
);
122132

123133
//Generate global variables
124134
let llvm_gv_index =

src/codegen/generators/variable_generator.rs

Lines changed: 84 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@ use crate::{
55
codegen::{debug::Debug, llvm_index::LlvmTypedIndex, llvm_typesystem::cast_if_needed},
66
index::{get_initializer_name, Index, PouIndexEntry, VariableIndexEntry},
77
resolver::{AnnotationMap, AstAnnotations, Dependency},
8+
ConfigFormat,
89
};
9-
10-
use inkwell::{module::Module, values::GlobalValue};
10+
use indexmap::IndexSet;
11+
use inkwell::{module::Module, types::BasicTypeEnum, values::GlobalValue};
1112
use plc_ast::ast::LinkageType;
1213
use plc_diagnostics::diagnostics::Diagnostic;
14+
use std::collections::HashMap;
15+
use std::fs::{read_to_string, write};
16+
use std::path::Path;
1317

1418
use super::{
1519
data_type_generator::get_default_for,
@@ -20,13 +24,48 @@ use super::{
2024
use crate::codegen::debug::DebugBuilderEnum;
2125
use crate::index::FxIndexSet;
2226

27+
pub fn read_got_layout(location: &str, format: ConfigFormat) -> Result<HashMap<String, u64>, Diagnostic> {
28+
if !Path::new(location).is_file() {
29+
// Assume if the file doesn't exist that there is no existing GOT layout yet. write_got_layout will handle
30+
// creating our file when we want to.
31+
return Ok(HashMap::new());
32+
}
33+
34+
let s =
35+
read_to_string(location).map_err(|_| Diagnostic::new("GOT layout could not be read from file"))?;
36+
match format {
37+
ConfigFormat::JSON => serde_json::from_str(&s)
38+
.map_err(|_| Diagnostic::new("Could not deserialize GOT layout from JSON")),
39+
ConfigFormat::TOML => {
40+
toml::de::from_str(&s).map_err(|_| Diagnostic::new("Could not deserialize GOT layout from TOML"))
41+
}
42+
}
43+
}
44+
45+
pub fn write_got_layout(
46+
got_entries: HashMap<String, u64>,
47+
location: &str,
48+
format: ConfigFormat,
49+
) -> Result<(), Diagnostic> {
50+
let s = match format {
51+
ConfigFormat::JSON => serde_json::to_string(&got_entries)
52+
.map_err(|_| Diagnostic::new("Could not serialize GOT layout to JSON"))?,
53+
ConfigFormat::TOML => toml::ser::to_string(&got_entries)
54+
.map_err(|_| Diagnostic::new("Could not serialize GOT layout to TOML"))?,
55+
};
56+
57+
write(location, s).map_err(|_| Diagnostic::new("GOT layout could not be written to file"))?;
58+
Ok(())
59+
}
60+
2361
pub struct VariableGenerator<'ctx, 'b> {
2462
module: &'b Module<'ctx>,
2563
llvm: &'b Llvm<'ctx>,
2664
global_index: &'b Index,
2765
annotations: &'b AstAnnotations,
2866
types_index: &'b LlvmTypedIndex<'ctx>,
2967
debug: &'b mut DebugBuilderEnum<'ctx>,
68+
got_layout_file: Option<(String, ConfigFormat)>,
3069
}
3170

3271
impl<'ctx, 'b> VariableGenerator<'ctx, 'b> {
@@ -37,8 +76,9 @@ impl<'ctx, 'b> VariableGenerator<'ctx, 'b> {
3776
annotations: &'b AstAnnotations,
3877
types_index: &'b LlvmTypedIndex<'ctx>,
3978
debug: &'b mut DebugBuilderEnum<'ctx>,
79+
got_layout_file: Option<(String, ConfigFormat)>,
4080
) -> Self {
41-
VariableGenerator { module, llvm, global_index, annotations, types_index, debug }
81+
VariableGenerator { module, llvm, global_index, annotations, types_index, debug, got_layout_file }
4282
}
4383

4484
pub fn generate_global_variables(
@@ -76,7 +116,7 @@ impl<'ctx, 'b> VariableGenerator<'ctx, 'b> {
76116
}
77117
});
78118

79-
for (name, variable) in globals {
119+
for (name, variable) in &globals {
80120
let linkage =
81121
if !variable.is_in_unit(location) { LinkageType::External } else { variable.get_linkage() };
82122
let global_variable = self.generate_global_variable(variable, linkage).map_err(|err| {
@@ -100,6 +140,46 @@ impl<'ctx, 'b> VariableGenerator<'ctx, 'b> {
100140
);
101141
}
102142

143+
if let Some((location, format)) = &self.got_layout_file {
144+
let got_entries = read_got_layout(location.as_str(), *format)?;
145+
let mut new_globals = Vec::new();
146+
let mut new_got_entries = HashMap::new();
147+
let mut new_got = HashMap::new();
148+
149+
for (name, _) in &globals {
150+
if let Some(idx) = got_entries.get(&name.to_string()) {
151+
new_got_entries.insert(name.to_string(), *idx);
152+
new_got.insert(*idx, name.to_string());
153+
} else {
154+
new_globals.push(name.to_string());
155+
}
156+
}
157+
158+
// Put any globals that weren't there last time in any free space in the GOT.
159+
let mut idx: u64 = 0;
160+
for name in &new_globals {
161+
while new_got.contains_key(&idx) {
162+
idx += 1;
163+
}
164+
new_got_entries.insert(name.to_string(), idx);
165+
new_got.insert(idx, name.to_string());
166+
}
167+
168+
// Now we can write new_got_entries back out to a file.
169+
write_got_layout(new_got_entries, location.as_str(), *format)?;
170+
171+
// Construct our GOT as a new global array. We initialise this array in the loader code.
172+
let got_size = new_got.keys().max().map_or(0, |m| *m + 1);
173+
let _got = self.llvm.create_global_variable(
174+
self.module,
175+
"__custom_got",
176+
BasicTypeEnum::ArrayType(Llvm::get_array_type(
177+
BasicTypeEnum::PointerType(self.llvm.context.i8_type().ptr_type(0.into())),
178+
got_size.try_into().expect("the computed custom GOT size is too large"),
179+
)),
180+
);
181+
}
182+
103183
Ok(index)
104184
}
105185

0 commit comments

Comments
 (0)