From 8ed13a394956a42ab98a3fe3843f0c0849c5be4d Mon Sep 17 00:00:00 2001 From: Angus Bethke Date: Wed, 17 Dec 2025 09:52:12 +0000 Subject: [PATCH] fix: moved shl and shr library functions to builtin and made them compatible with integers --- .../stdlib/iec61131-st/bit_shift_functions.st | 35 ------------ libs/stdlib/src/bit_shift_functions.rs | 56 ------------------- .../stdlib/tests/bit_shift_functions_tests.rs | 45 ++++++++++++++- libs/stdlib/tests/common/mod.rs | 8 --- src/builtins.rs | 52 +++++++++++++++++ xtask/res/combined.st | 33 ----------- 6 files changed, 95 insertions(+), 134 deletions(-) diff --git a/libs/stdlib/iec61131-st/bit_shift_functions.st b/libs/stdlib/iec61131-st/bit_shift_functions.st index 74676fef601..9d8b82eea92 100644 --- a/libs/stdlib/iec61131-st/bit_shift_functions.st +++ b/libs/stdlib/iec61131-st/bit_shift_functions.st @@ -1,38 +1,3 @@ -(************************** - * - * SHL(IN, n) - * - * This operator implements a bitwise shift of an operand to the left. - * IN is shifted by n bit to the left and is filled from the right with zeros. - * -*************************) -{external} -FUNCTION SHL : T -VAR_INPUT - IN : T; - n : UDINT; -END_VAR -END_FUNCTION - -(************************** - * - * SHR(IN, n) - * - * This operator implements a bitwise shift of an operand to the right. - * IN is shifted by n bit to the right. - * If an unsigned data type is used, filling from the left with zeros ensues. - * In the case of signed data types, an arithmetic shifting is implemented, - * i.e. it is filled with the value of the highest bit. - * -*************************) -{external} -FUNCTION SHR : T -VAR_INPUT - IN : T; - n : UDINT; -END_VAR -END_FUNCTION - (************************** * * ROL(IN, n) diff --git a/libs/stdlib/src/bit_shift_functions.rs b/libs/stdlib/src/bit_shift_functions.rs index b130d3dd175..918db6cf1f6 100644 --- a/libs/stdlib/src/bit_shift_functions.rs +++ b/libs/stdlib/src/bit_shift_functions.rs @@ -1,61 +1,5 @@ //! Defines shift operations -#[allow(non_snake_case)] -#[no_mangle] -/// Shift left operation on bytes -pub fn SHL__BYTE(input: u8, n: u32) -> u8 { - input << n -} - -#[allow(non_snake_case)] -#[no_mangle] -/// Shift left operation on word -pub fn SHL__WORD(input: u16, n: u32) -> u16 { - input << n -} - -#[allow(non_snake_case)] -#[no_mangle] -/// Shift left operation on dword -pub fn SHL__DWORD(input: u32, n: u32) -> u32 { - input << n -} - -#[allow(non_snake_case)] -#[no_mangle] -/// Shift left operation on lword -pub fn SHL__LWORD(input: u64, n: u32) -> u64 { - input << n -} - -#[allow(non_snake_case)] -#[no_mangle] -/// Shift right operation on bytes -pub fn SHR__BYTE(input: u8, n: u32) -> u8 { - input >> n -} - -#[allow(non_snake_case)] -#[no_mangle] -/// Shift right operation on word -pub fn SHR__WORD(input: u16, n: u32) -> u16 { - input >> n -} - -#[allow(non_snake_case)] -#[no_mangle] -/// Shift right operation on dword -pub fn SHR__DWORD(input: u32, n: u32) -> u32 { - input >> n -} - -#[allow(non_snake_case)] -#[no_mangle] -/// Shift right operation on lword -pub fn SHR__LWORD(input: u64, n: u32) -> u64 { - input >> n -} - #[allow(non_snake_case)] #[no_mangle] /// Rotate left operation on bytes diff --git a/libs/stdlib/tests/bit_shift_functions_tests.rs b/libs/stdlib/tests/bit_shift_functions_tests.rs index 72b9fa9cb8a..bd3a4b46b88 100644 --- a/libs/stdlib/tests/bit_shift_functions_tests.rs +++ b/libs/stdlib/tests/bit_shift_functions_tests.rs @@ -2,6 +2,7 @@ mod common; use common::add_std; +use plc_source::SourceCode; use crate::common::compile_and_run; @@ -30,7 +31,7 @@ fn shift_left_test() { l := SHL(LWORD#2#0001_1001,59); END_PROGRAM "; - let sources = add_std!(src, "bit_shift_functions.st"); + let sources = SourceCode::new(src, "main.st"); let mut maintype = MainType::default(); let _res: u32 = compile_and_run(sources, &mut maintype); assert_eq!(maintype.byte, 0b1100_1000); @@ -58,7 +59,7 @@ fn shift_right_test() { l := SHR(LWORD#16#1_0000_0000_0001,3); END_PROGRAM "; - let sources = add_std!(src, "bit_shift_functions.st"); + let sources = SourceCode::new(src, "main.st"); let mut maintype = MainType::default(); let _res: u32 = compile_and_run(sources, &mut maintype); assert_eq!(maintype.byte, 0x2); @@ -116,3 +117,43 @@ fn rotate_right_test() { assert_eq!(maintype.dword, 0x3000_0000); assert_eq!(maintype.lword, 0x3000_0000_0000_0000); } + +#[derive(Default, Debug)] +#[repr(C)] +struct MainTypePrg3569 { + a: u32, +} + +#[test] +fn bug_prg_3569_shl_must_be_usable_with_int() { + let src = " + PROGRAM main + VAR + a : INT; + END_VAR + a := SHL(21,2); + END_PROGRAM + "; + let sources = SourceCode::new(src, "main.st"); + let mut maintype = MainTypePrg3569::default(); + let _res: u32 = compile_and_run(sources, &mut maintype); + let shift_left = 21 << 2; + assert_eq!(maintype.a, shift_left); +} + +#[test] +fn bug_prg_3569_shr_must_be_usable_with_int() { + let src = " + PROGRAM main + VAR + a : INT; + END_VAR + a := SHR(21,2); + END_PROGRAM + "; + let sources = SourceCode::new(src, "main.st"); + let mut maintype = MainTypePrg3569::default(); + let _res: u32 = compile_and_run(sources, &mut maintype); + let shift_right = 21 >> 2; + assert_eq!(maintype.a, shift_right); +} diff --git a/libs/stdlib/tests/common/mod.rs b/libs/stdlib/tests/common/mod.rs index 88b09096d91..c9944a69c69 100644 --- a/libs/stdlib/tests/common/mod.rs +++ b/libs/stdlib/tests/common/mod.rs @@ -90,14 +90,6 @@ pub fn compile_with_native(context: &CodegenContext, source: T) - ("WCHAR_TO_WSTRING", iec61131std::string_conversion::WCHAR_TO_WSTRING as usize), ("WCHAR_TO_CHAR", iec61131std::string_conversion::WCHAR_TO_CHAR as usize), ("CHAR_TO_WCHAR", iec61131std::string_conversion::CHAR_TO_WCHAR as usize), - ("SHL__BYTE", iec61131std::bit_shift_functions::SHL__BYTE as usize), - ("SHL__WORD", iec61131std::bit_shift_functions::SHL__WORD as usize), - ("SHL__DWORD", iec61131std::bit_shift_functions::SHL__DWORD as usize), - ("SHL__LWORD", iec61131std::bit_shift_functions::SHL__LWORD as usize), - ("SHR__BYTE", iec61131std::bit_shift_functions::SHR__BYTE as usize), - ("SHR__WORD", iec61131std::bit_shift_functions::SHR__WORD as usize), - ("SHR__DWORD", iec61131std::bit_shift_functions::SHR__DWORD as usize), - ("SHR__LWORD", iec61131std::bit_shift_functions::SHR__LWORD as usize), ("ROL__BYTE", iec61131std::bit_shift_functions::ROL__BYTE as usize), ("ROL__WORD", iec61131std::bit_shift_functions::ROL__WORD as usize), ("ROL__DWORD", iec61131std::bit_shift_functions::ROL__DWORD as usize), diff --git a/src/builtins.rs b/src/builtins.rs index 9a23752eb46..1be7ea3d830 100644 --- a/src/builtins.rs +++ b/src/builtins.rs @@ -604,6 +604,58 @@ lazy_static! { } } ), + ( + "SHL", + BuiltIn { + decl: " + FUNCTION SHL : T + VAR_INPUT + IN : T; + n : UDINT; + END_VAR + END_FUNCTION + ", + annotation: None, + validation: Some(|validator, operator, parameters, _, _| { + validate_argument_count(validator, operator, ¶meters, 2); + }), + generic_name_resolver: no_generic_name_resolver, + code: |generator, params, _| { + let left = generator.generate_expression(params[0])?.into_int_value(); + let right = generator.generate_expression(params[1])?.into_int_value(); + + let shl = generator.llvm.builder.build_left_shift(left, right, "")?; + + Ok(ExpressionValue::RValue(shl.as_basic_value_enum())) + } + }, + ), + ( + "SHR", + BuiltIn { + decl: " + FUNCTION SHR : T + VAR_INPUT + IN : T; + n : UDINT; + END_VAR + END_FUNCTION + ", + annotation: None, + validation: Some(|validator, operator, parameters, _, _| { + validate_argument_count(validator, operator, ¶meters, 2); + }), + generic_name_resolver: no_generic_name_resolver, + code: |generator, params, _| { + let left = generator.generate_expression(params[0])?.into_int_value(); + let right = generator.generate_expression(params[1])?.into_int_value(); + + let shl = generator.llvm.builder.build_right_shift(left, right, false, "")?; + + Ok(ExpressionValue::RValue(shl.as_basic_value_enum())) + } + }, + ), ]); } diff --git a/xtask/res/combined.st b/xtask/res/combined.st index b74483abf4e..a26da3e4da2 100644 --- a/xtask/res/combined.st +++ b/xtask/res/combined.st @@ -914,39 +914,6 @@ FUNCTION DAY_OF_WEEK : SINT VAR_INPUT in : DATE; END_VAR -END_FUNCTION (* ************************* - * - * SHL(IN, n) - * - * This operator implements a bitwise shift of an operand to the left. - * IN is shifted by n bit to the left and is filled from the right with zeros. - * -************************ *) -{external} -FUNCTION SHL < T : ANY_BIT > : T -VAR_INPUT - IN : T; - n : UDINT; -END_VAR -END_FUNCTION - -(************************** - * - * SHR(IN, n) - * - * This operator implements a bitwise shift of an operand to the right. - * IN is shifted by n bit to the right. - * If an unsigned data type is used, filling from the left with zeros ensues. - * In the case of signed data types, an arithmetic shifting is implemented, - * i.e. it is filled with the value of the highest bit. - * -************************ *) -{external} -FUNCTION SHR < T : ANY_BIT > : T -VAR_INPUT - IN : T; - n : UDINT; -END_VAR END_FUNCTION (**************************