Interfacing with C, Inline Assembly & Performance
Or declare it yourself: function MessageBoxW(hWnd: THandle; lpText, lpCaption: PWideChar; uType: Cardinal): Integer; stdcall; external 'user32.dll';
36.1 Calling C library functions
cdecl (C), stdcall/winapi (Windows API) external 'libname' (name 'symbol' if names differ)
ctypes unit: cint, clong, cuint, cdouble; SizeUInt/NativeUInt for size_t; PAnsiChar for char*
record with {$PACKRECORDS C} (FPC) or matching {$ALIGN} (Delphi) pass PAnsiChar(UTF8String(S)); copy results with StrPas or string(P)
Pascal function with cdecl, passed with @ {$L file.o} / {$LINKLIB name} (FPC); {$L file.obj} (Delphi)
h2pas (FPC), Chet (C header translator for Delphi), manual translation
program CallC;
{$mode objfpc}{$H+}
uses ctypes; // C-compatible types: cint, clong, cuint, cdouble, ...
const
{$IFDEF WINDOWS}
LibC = 'msvcrt.dll';
{$ELSE}
LibC = 'c'; // libc.so / libSystem on macOS
{$ENDIF}
function c_strlen(s: PAnsiChar): SizeUInt; cdecl; external LibC name 'strlen'; // size_t
function c_abs(x: cint): cint; cdecl; external LibC name 'abs';
type
TCompareFunc = function(a, b: Pointer): cint; cdecl;
procedure c_qsort(base: Pointer; num, size: SizeUInt; compar: TCompareFunc);
cdecl; external LibC name 'qsort';
function CompareInts(a, b: Pointer): cint; cdecl; // a Pascal callback for C
begin
Result := PInteger(a)^ - PInteger(b)^;
end;
var
Data: array[0..5] of Integer = (42, 7, 19, 3, 88, 1);
I: Integer;
begin
Writeln(c_strlen('Hello from C')); // 12
Writeln(c_abs(-5)); // 5
c_qsort(@Data[0], Length(Data), SizeOf(Integer), @CompareInts);
for I in Data do Write(I, ' '); // 1 3 7 19 42 88
Writeln;
end.36.2 Windows API
Or declare it yourself: function MessageBoxW(hWnd: THandle; lpText, lpCaption: PWideChar; uType: Cardinal): Integer; stdcall; external 'user32.dll';
uses Windows; // Delphi: Winapi.Windows
begin
MessageBox(0, 'Hello from the Win32 API', 'Pascal', MB_OK or MB_ICONINFORMATION);
end.36.3 Dynamic loading
Delphi also supports external 'lib.dll' delayed; to load a library on first use.
uses dynlibs; // FPC; Delphi: LoadLibrary/GetProcAddress/FreeLibrary from Winapi.Windows or SysUtils (SafeLoadLibrary)
type
TAddFunc = function(A, B: Integer): Integer; cdecl;
var
H: TLibHandle;
Add: TAddFunc;
begin
H := LoadLibrary('mathlib.' + SharedSuffix); // SharedSuffix: 'dll', 'so', or 'dylib'
if H = NilHandle then Halt(1);
try
Add := TAddFunc(GetProcedureAddress(H, 'AddInts'));
if Assigned(Add) then Writeln(Add(2, 3));
finally
UnloadLibrary(H);
end;
end.36.4 Inline assembly
FPC: {$asmmode intel} or {$asmmode att}; supports x86, x86-64, ARM, AArch64, and other CPUs.
Delphi: Win32 allows asm blocks inside Pascal routines; Win64 allows only routines written entirely in assembler; ARM/mobile/Linux targets have no inline assembler.
Prefer compiler intrinsics and well-written Pascal: modern optimizers are good, and assembly is unportable and hard to maintain. Use it for SIMD hot loops after profiling.
{$mode objfpc}
{$IFDEF CPUX86_64}
{$asmmode intel}
function AddAsm(A, B: LongInt): LongInt; assembler; nostackframe;
asm
{$IFDEF WINDOWS}
lea eax, [ecx + edx] // Win64 ABI: first args in RCX, RDX
{$ELSE}
lea eax, [edi + esi] // System V ABI (Linux, macOS): RDI, RSI
{$ENDIF}
end;
{$ENDIF}36.5 Performance checklist
Measure first (GetTickCount64, Delphi TStopwatch, profilers) intuition about hot spots is often wrong
Better algorithms and data structures (TDictionary vs linear search)
const parameters for strings, records, interfaces avoids copies and ref-count traffic
Pre-size: SetLength, TList<T>.Capacity, TStringBuilder avoids repeated reallocation
Avoid Variant, Format and string concatenation in hot loops slow, allocate memory
Records instead of classes for small value objects no heap allocation inline small routines removes call overhead
Release builds: -O3 (FPC), optimization on, {$R-}{$Q-} checks cost time Avoid exceptions for control flow raising is expensive
Memory managers: FastMM (Delphi default); FPC cmem or alternative managers for multithreaded workloads
Profilers: Valgrind/Callgrind, perf (Linux, FPC), Intel VTune, AQtime, Delphi sampling profilers find real bottlenecks
uses SysUtils;
var
T0: QWord;
I: Integer;
S: Double;
begin
T0 := GetTickCount64;
S := 0;
for I := 1 to 50000000 do
S := S + Sqrt(I);
Writeln('Sum = ', S:0:0, ' in ', GetTickCount64 - T0, ' ms');
end.