Bit-shift functions
⚠️ matiec SHL emit-bug
Before reading the rest of this page: SHL(BYTE#1, …) is
broken in matiec. The C-code generator emits a data__->BYTE
struct-member access where it should emit an integer literal.
The local FStCompiler accepts the call; only the PLC-side g++
compile fails. See llm_signals for the workaround.
The bug only triggers when the first argument is a typed
literal like BYTE#1. Workarounds:
- Use an
IF/ELSIFcascade orCASE OFover decimal literals (recommended — explicit, matiec-clean). - Pre-compute the value with
INT_TO_BYTE(1)and store in a local; pass the local to SHL. This may also work depending on the matiec version, but the IF cascade is reliable.
The same emit-bug doesn’t affect SHR, ROL, ROR in our
testing — but the safe pattern is the same.
SHL — shift left
SHL(IN: ANY_BIT, N: UINT): ANY_BIT — shifts IN left by
N positions; vacated bits are zero.
bResult := SHL(bByte, 2); (* multiply by 4 *)
wResult := SHL(wWord, 8); (* shift the low byte to the high byte *)
IN and result are the same ANY_BIT type
(BYTE/WORD/DWORD/LWORD). N is UINT.
Bits shifted off the high end are lost — there is no “sticky” or carry behaviour.
SHR — shift right
SHR(IN: ANY_BIT, N: UINT): ANY_BIT — shifts right; vacated
bits are zero (logical shift, not arithmetic).
bResult := SHR(bByte, 2); (* divide by 4, unsigned *)
ROL — rotate left
ROL(IN: ANY_BIT, N: UINT): ANY_BIT — bits shifted off the
high end wrap around to the low end.
bResult := ROL(2#1000_0001, 1); (* → 2#0000_0011 *)
ROR — rotate right
ROR(IN: ANY_BIT, N: UINT): ANY_BIT — bits shifted off the
low end wrap to the high end.
bResult := ROR(2#1000_0001, 1); (* → 2#1100_0000 *)
When NOT to use shifts
For bit-mask construction by index (the canonical
1 << N pattern), prefer one of:
An
IF/ELSIFcascade with explicit decimal literals (always matiec-safe).A
CASE OFwith decimal labels — but watch out for the hex-literal case-label quirk documented at textual/structured-text/control-flow/case-statement.A const lookup table:
VAR CONSTANT arrMask : ARRAY[0..7] OF BYTE := [1, 2, 4, 8, 16, 32, 64, 128]; END_VAR bMask := arrMask[iStep];
For bit-by-bit access of a BYTE/WORD/DWORD/LWORD,
prefer the bit-access syntax over a shift+mask pattern:
xBit := bByte.3; (* read bit 3 as BOOL — clean, type-checked *)
bByte.5 := TRUE; (* write bit 5 *)
See bitwise operators.
IEC reference
IEC 61131-3 third edition (2013), Annex F.2.3 — “Bit-string functions” Table F.7.
matiec conformance
SHL has the documented emit-bug with typed-literal first
arguments — see llm_signals. SHR, ROL, ROR work as
expected in matiec at the time of writing.
A future matiec++ or rustly backend (see News: open language backend) is expected to make this quirk go away.