Skip to content

Commit b2e3a36

Browse files
authored
Merge branch 'master' into master
2 parents 4e44308 + 16b756f commit b2e3a36

File tree

449 files changed

+5002
-4199
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

449 files changed

+5002
-4199
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ tempfile = "3"
5656
encoding_rs.workspace = true
5757
encoding_rs_io.workspace = true
5858

59-
6059
[lib]
6160
name = "rusty"
6261
path = "src/lib.rs"

book/src/variables.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,61 @@ PROGRAM PLC_PRG
5757
...
5858
END_PROGRAM
5959
```
60+
61+
### Pointer Initialization
62+
63+
A pointer variable can be initialized with the address of a global reference or an IEC-address using the `AT` or `REFERENCE TO` syntax. `REF_TO` pointers can be initialized using the built-in `REF` function in its initializer.
64+
65+
This initialization, however, does not take place during compile time. Instead, each pointer initialized with an address will be zero-initialized to a null pointer by default. The compiler collects all pointer initializations during compilation and creates internal initializer functions for each POU. These functions are then called in a single overarching project-initialization function, which can be called either manually in your main function or by a runtime. Additionally, global variables — whether they are initialized pointers or POU instances containing pointer initializers — are also assigned within this overarching function.
66+
67+
This function follows a naming scheme (`__init___<project name>`) that varies slightly depending on whether a build config (`plc.json`) was used.
68+
69+
- **When using a build config (`plc.json`)**, the project name is used:
70+
71+
_Build config snippet:_
72+
```json
73+
{
74+
"name": "myProject",
75+
"files": []
76+
}
77+
```
78+
_Resulting symbol:_
79+
```iecst
80+
__init___myProject()
81+
```
82+
83+
- **When compiling without a build config**, the name of the first file passed via CLI is used as the base for the name.
84+
85+
_CLI command:_
86+
```bash
87+
# build command
88+
plc myFile1.st myFile2.st
89+
```
90+
_Resulting symbol:_
91+
```iecst
92+
__init___myFile1_st()
93+
```
94+
95+
It is important to note that if there are pointer initializations present in your project, failing to call the initialization function in your runtime or in `main` will result in **null pointer dereferences** at runtime.
96+
97+
### Example
98+
_myProject.st_:
99+
```iecst
100+
VAR_GLOBAL
101+
myGlobal : STRING;
102+
END_VAR
103+
104+
PROGRAM prog
105+
VAR
106+
myString : REF_TO STRING := REF(myGlobal);
107+
myOtherString : REFERENCE TO STRING REF= myGlobal;
108+
myAlias AT myGlobal: STRING;
109+
myAnalogSignal AT %IX1.0 : REAL;
110+
END_VAR
111+
// ...
112+
END_PROGRAM
113+
114+
FUNCTION main: DINT
115+
__init___myProject_st();
116+
prog();
117+
END_FUNCTION

compiler/plc_driver/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ plc_index = { path = "../plc_index" }
1717

1818
serde = { version = "1.0", features = ["derive"] }
1919
serde_json = "1"
20+
toml = "0.5"
2021
clap = { version = "3.0", features = ["derive"] }
2122
rayon = "1.6.1"
2223
tempfile = "3"

compiler/plc_driver/src/cli.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ use encoding_rs::Encoding;
55
use plc_diagnostics::diagnostics::{diagnostics_registry::DiagnosticsConfiguration, Diagnostic};
66
use std::{env, ffi::OsStr, num::ParseIntError, path::PathBuf};
77

8-
use plc::{output::FormatOption, ConfigFormat, DebugLevel, ErrorFormat, Target, Threads};
8+
use plc::output::FormatOption;
9+
use plc::{ConfigFormat, DebugLevel, ErrorFormat, Target, Threads, DEFAULT_GOT_LAYOUT_FILE};
910

1011
pub type ParameterError = clap::Error;
1112

@@ -109,6 +110,22 @@ pub struct CompileParameters {
109110
) ]
110111
pub hardware_config: Option<String>,
111112

113+
#[clap(
114+
name = "got-layout-file",
115+
long,
116+
global = true,
117+
help = "Obtain information about the current custom GOT layout from the given file if it exists.
118+
Save information about the generated custom GOT layout to the given file.
119+
Format is detected by extension.
120+
Supported formats : json, toml",
121+
default_value = DEFAULT_GOT_LAYOUT_FILE,
122+
parse(try_from_str = validate_config),
123+
// FIXME: For some reason, this does not work at the moment but it really should
124+
// The binary behaves as expected but the tests fail
125+
// requires = "online-change"
126+
) ]
127+
pub got_layout_file: String,
128+
112129
#[clap(
113130
name = "optimization",
114131
long,
@@ -202,6 +219,12 @@ pub struct CompileParameters {
202219
#[clap(name = "check", long, help = "Check only, do not generate any output", global = true)]
203220
pub check_only: bool,
204221

222+
#[clap(
223+
long,
224+
help = "Emit a binary with specific compilation information, suitable for online changes when ran under a conforming runtime"
225+
)]
226+
pub online_change: bool,
227+
205228
#[clap(subcommand)]
206229
pub commands: Option<SubCommands>,
207230
}
@@ -388,6 +411,11 @@ impl CompileParameters {
388411
self.hardware_config.as_deref().and_then(get_config_format)
389412
}
390413

414+
pub fn got_layout_format(&self) -> ConfigFormat {
415+
// It is safe to unwrap here, since the provided argument to `--got-online-change` has been checked with `validate_config`
416+
get_config_format(&self.got_layout_file).unwrap()
417+
}
418+
391419
/// Returns the location where the build artifacts should be stored / output
392420
pub fn get_build_location(&self) -> Option<PathBuf> {
393421
match &self.commands {

compiler/plc_driver/src/lib.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use std::{
1919

2020
use cli::{CompileParameters, ParameterError, SubCommands};
2121
use plc::{
22-
codegen::CodegenContext, linker::LinkerType, output::FormatOption, DebugLevel, ErrorFormat,
22+
codegen::CodegenContext, linker::LinkerType, output::FormatOption, DebugLevel, ErrorFormat, OnlineChange,
2323
OptimizationLevel, Target, Threads,
2424
};
2525

@@ -54,6 +54,7 @@ pub struct CompileOptions {
5454
pub error_format: ErrorFormat,
5555
pub debug_level: DebugLevel,
5656
pub single_module: bool,
57+
pub online_change: OnlineChange,
5758
}
5859

5960
impl Default for CompileOptions {
@@ -67,6 +68,7 @@ impl Default for CompileOptions {
6768
error_format: ErrorFormat::None,
6869
debug_level: DebugLevel::None,
6970
single_module: false,
71+
online_change: OnlineChange::Disabled,
7072
}
7173
}
7274
}
@@ -176,6 +178,14 @@ pub fn get_compilation_context<T: AsRef<str> + AsRef<OsStr> + Debug>(
176178
error_format: compile_parameters.error_format,
177179
debug_level: compile_parameters.debug_level(),
178180
single_module: compile_parameters.single_module,
181+
online_change: if compile_parameters.online_change {
182+
OnlineChange::Enabled {
183+
file_name: compile_parameters.got_layout_file.clone(),
184+
format: compile_parameters.got_layout_format(),
185+
}
186+
} else {
187+
OnlineChange::Disabled
188+
},
179189
};
180190

181191
let libraries =

compiler/plc_driver/src/pipelines.rs

Lines changed: 81 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
use std::{
2+
collections::HashMap,
23
env,
34
fs::{self, File},
45
io::Write,
56
path::{Path, PathBuf},
7+
sync::Mutex,
68
};
79

810
use crate::{CompileOptions, LinkOptions};
@@ -22,7 +24,7 @@ use plc::{
2224
TypeAnnotator,
2325
},
2426
validation::Validator,
25-
ConfigFormat, Target,
27+
ConfigFormat, OnlineChange, Target,
2628
};
2729
use plc_diagnostics::{
2830
diagnostician::Diagnostician,
@@ -36,6 +38,42 @@ use project::{
3638
use rayon::prelude::*;
3739
use source_code::{source_location::SourceLocation, SourceContainer};
3840

41+
use serde_json;
42+
use toml;
43+
44+
pub fn read_got_layout(location: &str, format: ConfigFormat) -> Result<HashMap<String, u64>, Diagnostic> {
45+
if !Path::new(location).is_file() {
46+
// Assume if the file doesn't exist that there is no existing GOT layout yet. write_got_layout will handle
47+
// creating our file when we want to.
48+
return Ok(HashMap::new());
49+
}
50+
51+
let s = fs::read_to_string(location)
52+
.map_err(|_| Diagnostic::new("GOT layout could not be read from file"))?;
53+
match format {
54+
ConfigFormat::JSON => serde_json::from_str(&s)
55+
.map_err(|_| Diagnostic::new("Could not deserialize GOT layout from JSON")),
56+
ConfigFormat::TOML => {
57+
toml::de::from_str(&s).map_err(|_| Diagnostic::new("Could not deserialize GOT layout from TOML"))
58+
}
59+
}
60+
}
61+
62+
fn write_got_layout(
63+
got_entries: HashMap<String, u64>,
64+
location: &str,
65+
format: ConfigFormat,
66+
) -> Result<(), Diagnostic> {
67+
let s = match format {
68+
ConfigFormat::JSON => serde_json::to_string(&got_entries)
69+
.map_err(|_| Diagnostic::new("Could not serialize GOT layout to JSON"))?,
70+
ConfigFormat::TOML => toml::ser::to_string(&got_entries)
71+
.map_err(|_| Diagnostic::new("Could not serialize GOT layout to TOML"))?,
72+
};
73+
74+
fs::write(location, s).map_err(|_| Diagnostic::new("GOT layout could not be written to file"))
75+
}
76+
3977
///Represents a parsed project
4078
///For this struct to be built, the project would have been parsed correctly and an AST would have
4179
///been generated
@@ -271,11 +309,18 @@ impl<T: SourceContainer + Sync> AnnotatedProject<T> {
271309
}
272310

273311
pub fn codegen_to_string(&self, compile_options: &CompileOptions) -> Result<Vec<String>, Diagnostic> {
312+
let got_layout = if let OnlineChange::Enabled { file_name, format } = &compile_options.online_change {
313+
read_got_layout(file_name, *format)?
314+
} else {
315+
HashMap::default()
316+
};
317+
let got_layout = Mutex::new(got_layout);
318+
274319
self.units
275320
.iter()
276321
.map(|AnnotatedUnit { unit, dependencies, literals }| {
277322
let context = CodegenContext::create();
278-
self.generate_module(&context, compile_options, unit, dependencies, literals)
323+
self.generate_module(&context, compile_options, unit, dependencies, literals, &got_layout)
279324
.map(|it| it.persist_to_string())
280325
})
281326
.collect()
@@ -286,11 +331,18 @@ impl<T: SourceContainer + Sync> AnnotatedProject<T> {
286331
context: &'ctx CodegenContext,
287332
compile_options: &CompileOptions,
288333
) -> Result<Option<GeneratedModule<'ctx>>, Diagnostic> {
334+
let got_layout = if let OnlineChange::Enabled { file_name, format } = &compile_options.online_change {
335+
read_got_layout(file_name, *format)?
336+
} else {
337+
HashMap::default()
338+
};
339+
let got_layout = Mutex::new(got_layout);
340+
289341
let Some(module) = self
290342
.units
291343
.iter()
292344
.map(|AnnotatedUnit { unit, dependencies, literals }| {
293-
self.generate_module(context, compile_options, unit, dependencies, literals)
345+
self.generate_module(context, compile_options, unit, dependencies, literals, &got_layout)
294346
})
295347
.reduce(|a, b| {
296348
let a = a?;
@@ -310,13 +362,16 @@ impl<T: SourceContainer + Sync> AnnotatedProject<T> {
310362
unit: &CompilationUnit,
311363
dependencies: &FxIndexSet<Dependency>,
312364
literals: &StringLiterals,
365+
got_layout: &Mutex<HashMap<String, u64>>,
313366
) -> Result<GeneratedModule<'ctx>, Diagnostic> {
314367
let mut code_generator = plc::codegen::CodeGen::new(
315368
context,
316369
compile_options.root.as_deref(),
317370
&unit.file_name,
318371
compile_options.optimization,
319372
compile_options.debug_level,
373+
//FIXME don't clone here
374+
compile_options.online_change.clone(),
320375
);
321376
//Create a types codegen, this contains all the type declarations
322377
//Associate the index type with LLVM types
@@ -326,8 +381,9 @@ impl<T: SourceContainer + Sync> AnnotatedProject<T> {
326381
literals,
327382
dependencies,
328383
&self.index,
384+
got_layout,
329385
)?;
330-
code_generator.generate(context, unit, &self.annotations, &self.index, &llvm_index)
386+
code_generator.generate(context, unit, &self.annotations, &self.index, llvm_index)
331387
}
332388

333389
pub fn codegen_single_module<'ctx>(
@@ -372,6 +428,14 @@ impl<T: SourceContainer + Sync> AnnotatedProject<T> {
372428
});
373429
ensure_compile_dirs(targets, &compile_directory)?;
374430
let targets = if targets.is_empty() { &[Target::System] } else { targets };
431+
432+
let got_layout = if let OnlineChange::Enabled { file_name, format } = &compile_options.online_change {
433+
read_got_layout(file_name, *format)?
434+
} else {
435+
HashMap::default()
436+
};
437+
let got_layout = Mutex::new(got_layout);
438+
375439
let res = targets
376440
.par_iter()
377441
.map(|target| {
@@ -409,8 +473,15 @@ impl<T: SourceContainer + Sync> AnnotatedProject<T> {
409473
};
410474

411475
let context = CodegenContext::create(); //Create a build location for the generated object files
412-
let module =
413-
self.generate_module(&context, compile_options, unit, dependencies, literals)?;
476+
let module = self.generate_module(
477+
&context,
478+
compile_options,
479+
unit,
480+
dependencies,
481+
literals,
482+
&got_layout,
483+
)?;
484+
414485
module
415486
.persist(
416487
Some(&compile_directory),
@@ -429,6 +500,10 @@ impl<T: SourceContainer + Sync> AnnotatedProject<T> {
429500
})
430501
.collect::<Result<Vec<_>, Diagnostic>>()?;
431502

503+
if let OnlineChange::Enabled { file_name, format } = &compile_options.online_change {
504+
write_got_layout(got_layout.into_inner().unwrap(), file_name, *format)?;
505+
}
506+
432507
Ok(res)
433508
}
434509

compiler/plc_driver/src/tests/snapshots/plc_driver__tests__external_files__external_file_function_call.snap

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ expression: "results.join(\"\\n\")"
55
; ModuleID = 'main.st'
66
source_filename = "main.st"
77

8-
define i16 @main() section "fn-$RUSTY$main:i16" {
8+
define i16 @main() {
99
entry:
1010
%main = alloca i16, align 2
1111
store i16 0, i16* %main, align 2
@@ -14,17 +14,17 @@ entry:
1414
ret i16 %main_ret
1515
}
1616

17-
declare i16 @external() section "fn-$RUSTY$external:i16"
17+
declare i16 @external()
1818

1919
; ModuleID = 'external.st'
2020
source_filename = "external.st"
2121

22-
declare i16 @external() section "fn-$RUSTY$external:i16"
22+
declare i16 @external()
2323

2424
; ModuleID = '__init___TestProject'
2525
source_filename = "__init___TestProject"
2626

27-
define void @__init___TestProject() section "fn-$RUSTY$__init___TestProject:v" {
27+
define void @__init___TestProject() {
2828
entry:
2929
ret void
3030
}

0 commit comments

Comments
 (0)