Can I just use the installer anew? Because that is a neat feature.
Yes - the installer handles both: fresh install or update of your current installation.
I have a question regarding the new int128...
"Normal FPC", when dealing with mixed signed expressions promotes types as needed up to int64. This causes problems when the expression uses qwords because there is no int128 "superset type" that could be used to "homogenize" the expression.
My question is: in unleashed, if the modeswitch int128 is enabled and an expression has item types int64 and qword in it, will the compiler automatically promote the types to int128 to do the calculations ?
Two separate cases here, because promotion happens per operator, not per expression.
Case 1: the expression only mixes Int64 and QWord, no 128 bit operand anywhereNo automatic promotion. It behaves exactly like stock FPC (both sides go through
Int64), with or without the modeswitch. I kept the switch away from existing expressions on purpose - code that compiles today must mean the same thing tomorrow. BTW, C compilers with
__int128 made the same call: an expression that mixes
int64_t and
uint64_t still resolves to
uint64_t (plain unsigned 64 bit), not
__int128 - the usual arithmetic conversions never reach for a wider type that isn't already in the expression.
Case 2: at least one operand is Int128/UInt128Then yes - promotion kicks in, pairwise at each operator. From the first operator that touches the 128 bit operand, everything downstream runs at 128 bits. The variable you assign to plays no role (Pascal has no target-typed evaluation), narrowing happens at the assignment itself: silent truncation by default, range error with
-Cr.
{$mode unleashed}
procedure main;
begin
var q: QWord := 18446744073709551615;
var a: Int128 := 2;
var n: Integer := 3;
var x: DWord;
x := q * a * n; // computed fully in 128 bit, then narrowed ->
writeln(x); // 4294967290
writeln(a * q * n); // 110680464442257309690 - 128 bit from the first operator
writeln(q * n * a); // 36893488147419103226 - surprise!
readln;
end;
begin
main;
end.
The last line is the one to watch: operators group left to right, so
q * n is evaluated first, at 64 bits, wraps around, and only the already-wrapped result gets promoted for the final multiply. Same rule C applies to
__int128. So to homogenize a mixed expression, put one
Int128(...) cast on the leftmost operand and the whole chain follows.
The compiler also warns you when a 64 bit multiply or add lands in a 128 bit slot, so the wrap does not pass unnoticed:
var
a, b: Int64;
c: Int128;
begin
c := a * b; // a*b is a 64 bit multiply, result widened afterwards
end;
Hint: Converting the operands to "Int128" before doing the multiply could prevent overflow errors.
Compile with
-vh, or watch the Messages window in Lazarus. It is the same hint FPC already gives on 32 bit targets when a
longint*longint result goes into an
Int64 -
Int128 just moves that safety net one level up.
Compile time constants are a different story: under the switch the constant evaluator works in full 128 bit arithmetic at every step, so
writeln(10000000000000000000 * 10 div 10000000000000000000) prints 10 even though the intermediate is 1e20, and the result constant is adapted back down to the smallest fitting type. Past 128 bits you get a compile error. Without the switch every folding step must stay inside the historical
Int64/
QWord envelope, exactly like stock.
To add: does the compiler optimize to avx2 instructions? Because that is the only benefit when using 128 bit integer types......
No, and no compiler does that - gcc and clang included. As @marcov pointed out, the registers themselves are wide (128 bit in SSE2, 256 bit in AVX2), but they are vectors of smaller elements, and the widest integer element is 64 bit. There is no 128 bit integer ALU behind them:
vpaddq adds independent 64 bit lanes and a carry never crosses a lane boundary. Emulating a single 128 bit addition in SIMD means extra instructions just to move the carry from the low lane into the high one - slower than the two instructions the scalar unit needs. SIMD pays off for arrays of independent values, not for one wide integer.
What Unleashed emits on x86_64 is a pair of 64 bit general purpose registers - the same model gcc and clang use for
__int128:
; a + b
add rax, r8
adc rdx, r9
; a * b
imul rcx, r9
imul rdx, r8
add rcx, rdx
mul r8
add rdx, rcx
add/sub, logic, compares, shifts, mul and the 64<->128 conversions are all inline sequences like the above. Only
div/
mod, overflow-checked
mul and
Str/
Val stay runtime helpers - which is also what gcc does with
__divti3.
Some numbers from my machine (at
-O2):
Int128 add: 0.44 ns/op (Int64 add: 0.22 ns/op)
Int128 mul: 1.33 ns/op (Int64 mul: 0.86 ns/op)
Roughly a factor of two - the same ratio
Int64 had against
LongInt back on 32 bit CPUs.
So the benefit is not vectorization. The benefit is a native 128 bit type that costs about twice its 64 bit sibling, plus on SysV targets parameters follow the C
__int128 ABI, so
cdecl interop with gcc/clang works out of the box.