Page 1 of 1
inline asm c back end
Posted: Wed Dec 01, 2021 10:19 pm
by idle
is it really going to be this easy to use asm inline with c back end?
Code: Select all
Procedure bswap(v.l)
Protected ret.l
CompilerIf #PB_Compiler_Backend = #PB_Backend_C
!".intel_syntax noprefix";
!"mov eax, v_v";
!"bswap eax";
!"mov v_ret, eax";
CompilerElse
!mov eax, [p.v_v]
!bswap eax
!mov [p.v_ret],eax
CompilerEndIf
ProcedureReturn ret
EndProcedure
x.i = $FF000000
Debug RSet(Hex(x),8,"0")
x = bswap(x)
Debug RSet(Hex(x),8,"0")
Edit: Answer no it's not, it wasn't working despite no compliant by the compiler.
Anyway here's an explanation of using asm templates.
https://www.felixcloutier.com/documents/gcc-asm.html
The statement structure boils down to this.
asm <optional stuff> (
"assembler template"
: outputs
: inputs
: clobbers
: labels)
so by following a output return variable pattern we can pretty much port existing asm without to much gnashing of teeth
Thanks Wilbert for a nudge in the right direction.
Code: Select all
; pbcompilerc -d test.pb
; pbcompiler -d test.pb
Macro AsmInput(var)
!:[var] "r" (v_#var)
EndMacro
Macro AsmInput2(var0,var1)
!:[var0] "r" (v_#var0),[var1] "r" (v_#var1)
EndMacro
Macro AsmInput3(var0,var1,var2)
!:[var0] "r" (v_#var0),[var1] "r" (v_#var1),[var2] "r" (v_#var2)
EndMacro
Macro AsmOutput(var)
!".att_syntax;"
!:[var] "=r" (v_#var)
EndMacro
Macro BeginAsm()
!__asm__(
!".intel_syntax noprefix;"
EndMacro
Macro EndAsm()
!);
EndMacro
Procedure bswap(v.l)
Protected ret.l = 0
CompilerIf #PB_Compiler_Backend = #PB_Backend_C
BeginAsm()
!"mov eax,%[v];"
!"bswap eax;"
!"mov %[ret], eax;"
AsmOutput(ret)
AsmInput(v)
EndAsm()
CompilerElse
!mov eax, [p.v_v]
!bswap eax
!mov [p.v_ret],eax
CompilerEndIf
ProcedureReturn ret
EndProcedure
Procedure shift_right(v.l, shift.l)
Protected ret.l =0
CompilerIf #PB_Compiler_Backend = #PB_Backend_C
BeginAsm()
!"mov eax,%[v];"
!"mov ecx,%[shift];"
!"shr eax,cl;"
!"mov %[ret],eax;"
AsmOutput(ret)
AsmInput2(v,shift)
EndAsm()
CompilerElse
!mov eax, [p.v_v]
!mov ecx, [p.v_shift]
!shr eax, cl
!mov [p.v_ret],eax
CompilerEndIf
ProcedureReturn ret
EndProcedure
Procedure absint(a.i)
Protected ret.i
CompilerIf #PB_Compiler_Backend = #PB_Backend_C
BeginAsm()
!"mov rdx,%[a];"
!"mov rax, rdx;"
!"neg rax;"
!"CMOVl rax,rdx;"
!"mov %[ret],rax;"
AsmOutput(ret)
AsmInput(a)
EndAsm()
CompilerElse
!mov rdx,[p.v_a]
!mov rax, rdx
!neg rax
!CMOVl rax,rdx
!mov [p.v_ret],rax
CompilerEndIf
ProcedureReturn ret
EndProcedure
x.i = $FF000000
Debug RSet(Hex(x),8,"0")
x= bswap(x)
Debug RSet(Hex(x),8,"0")
x = absint(-42)
Debug x
xx.l = $FF000000
Debug RSet(Bin(xx, #PB_Long),32,"0")
xx = shift_right(xx, 1)
Debug RSet(Bin(xx, #PB_Long),32,"0")
Re: inline asm c back end
Posted: Thu Dec 02, 2021 1:33 am
by StarBootics
I have try your code with the C backend and the compiler don't seem to complain but the swap is not working.
That being said the CPU I have in my computer is AMD Ryzen maybe that explain everything.
Fred himself said that inline assembler will be supported by the C backend compiler eventually not in version 6.00 but in the following one's and the ASM syntax will be little bit different.
But yeah, C compiler are capable to deal with ASM instructions.
Best regards
StarBootics
Re: inline asm c back end
Posted: Thu Dec 02, 2021 3:51 am
by idle
Yes I was a bit fast in posting it as I assumed it worked when it didn't complain, but I didn't notice that it wasn't working.
maybe it just needs a flag passing onto gcc, I was under the impression that the inline asm with c instrisic's was working.
what I didn't know was that it could be done with little change.
it still doens't work the assembly is being parsed so I don't what's wrong here.?
Code: Select all
Procedure bswap(v.l)
Protected ret.l
CompilerIf #PB_Compiler_Backend = #PB_Backend_C
!".intel_syntax noprefix";
!"mov eax, v_v";
!"bswap eax";
!"mov v_ret, eax";
CompilerElse
!mov eax, [p.v_v]
!bswap eax
!mov [p.v_ret],eax
CompilerEndIf
ProcedureReturn ret
EndProcedure
x.i = $FF000000
Debug RSet(Hex(x),8,"0")
x = bswap(x)
Debug RSet(Hex(x),8,"0")
generates c
Code: Select all
// PureBasic 6.00 Beta 1 - C Backend (Linux - x64) generated code
//
// (c) 2021 Fantaisie Software
//
// The header must remain intact for Re-Assembly
//
// :System
//
#pragma warning(disable: 4024)
//
typedef long long quad;
typedef quad integer;
#define PB_INFINITY (1.0 / 0.0)
#define PB_NEG_INFINITY (-1.0 / 0.0)
typedef struct pb_array { void *a; } pb_array;
typedef struct pb_array2 { void *a; integer b[2]; } pb_array2;
typedef struct pb_array3 { void *a; integer b[3]; } pb_array3;
typedef struct pb_array4 { void *a; integer b[4]; } pb_array4;
typedef struct pb_array5 { void *a; integer b[5]; } pb_array5;
typedef struct pb_array6 { void *a; integer b[6]; } pb_array6;
typedef struct pb_array7 { void *a; integer b[7]; } pb_array7;
typedef struct pb_array8 { void *a; integer b[8]; } pb_array8;
typedef struct pb_array9 { void *a; integer b[9]; } pb_array9;
typedef struct pb_listitem { void *a; void *b; void *c;} pb_listitem;
typedef struct pb_list { void *a; pb_listitem *b; } pb_list;
typedef struct pb_mapitem { void *a; void *b; void *c;} pb_mapitem;
typedef struct pb_pbmap { pb_mapitem *a; } pb_pbmap;
typedef struct pb_map { pb_pbmap *a; } pb_map;
static integer s_s[]={0, -1};
#define M_SYSFUNCTION(a) a
#define M_PBFUNCTION(a) a
#define M_CDECL
typedef void TCHAR;
static integer SYS_BankerRound (double i) { return i >= 0 ? i+0.5 : i-0.5; }
static quad SYS_BankerRoundQuad(double i) { return i >= 0 ? i+0.5 : i-0.5; }
//
static char *tls;
int PB_ExitCode=0;
integer PB_MemoryBase=0;
integer PB_Instance=0;
int PB_ArgC;
char **PB_ArgV;
//
//
//
//
void SYS_Quit();
M_SYSFUNCTION(void) SYS_InitPureBasic();
int PB_DEBUGGER_LineNumber=-1;
int PB_DEBUGGER_IncludedFiles=0;
char *PB_DEBUGGER_FileName=0;
//
static integer f_bswap(int v_v);
static integer ms_s[]={0,-1};
integer v_x=0;
//
//
// Procedure bswap(v.l)
static integer f_bswap(int v_v) {
integer r=0;
int v_ret=0;
// Protected ret.l ;
// CompilerIf #PB_Compiler_Backend = #PB_Backend_C
// !".intel_syntax noprefix";
".intel_syntax noprefix";
// !"mov eax, v_v";
"mov eax, v_v";
// !"bswap eax";
"bswap eax";
// !"mov v_ret, eax";
"mov v_ret, eax";
// CompilerElse
// ProcedureReturn ret
r=v_ret;
goto end;
// EndProcedure
end:
return r;
}
//
char PB_OpenGLSubsystem=0;
int PB_Compiler_Unicode=1;
int PB_Compiler_Thread=0;
int PB_Compiler_Purifier=0;
int PB_Compiler_Debugger=0;
int PB_Compiler_DPIAware=0;
int PB_Compiler_XPSkins=0;
int PB_ExecutableType=0;
//
void PB_EndFunctions() {
}
//
int main(int argc, char* argv[]) {
PB_ArgC = argc;
PB_ArgV = argv;
SYS_InitPureBasic();
//
//
// x.i = $FF000000
v_x=4278190080;
// Debug RSet(Hex(x),8,"0")
// x = bswap(x)
integer rr0=f_bswap(v_x);
v_x=rr0;
// Debug RSet(Hex(x),8,"0")
//
//
SYS_Quit();
}
void SYS_Quit() {
PB_EndFunctions();
exit(PB_ExitCode);
}
Re: inline asm c back end
Posted: Thu Dec 02, 2021 9:32 am
by StarBootics
Re: inline asm c back end
Posted: Thu Dec 02, 2021 9:51 am
by idle
Yes those helped a bit. I suspect it's a compiler flag causing it to fail. The syntax is checked but the asm seems to be ignored. Ive asked Fred about it but tomorrow I will use a stub gcc to capture the command line used and see if I can fix it that way
It would be a huge time saver if we can use this asm format.
Re: inline asm c back end
Posted: Thu Dec 02, 2021 12:02 pm
by juergenkulow
Code: Select all
; Demo Problem with v_y and v_v in asm - any idea? - only Windows x64
Procedure.l bswap(v.l,w.l)
Protected y.l=v ;00000001400010AF | 8945 F8 | mov dword ptr ss:[rbp-8],eax |
; .align 8
; v_y:
; .space 8
; .text
DisableDebugger
CompilerIf Defined(PB_Compiler_Backend,#PB_Constant)
CompilerIf #PB_Compiler_Backend = #PB_Backend_C
! asm("movl 16(%rbp),%eax\n\t bswap %eax \n\t movl %eax,24(%rbp)");
; ! asm("movl -8(%rbp),%eax\n\t bswap %eax \n\t movl %eax,24(%rbp)");
; ! asm("movl v_y(%rbp),%eax\n\t bswap %eax \n\t movl %eax,24(%rbp)");
; 00000001400010B2 | 8B85 30000000 | mov eax,dword ptr ss:[rbp+30] |
; ! asm("movl v_v(%rbp),%eax\n\t bswap %eax \n\t movl %eax,24(%rbp)");
; POLINK: error: Unresolved external symbol 'v_v' - referenced from 'PureBasic.obj'.
CompilerElse
CompilerError "Only #PB_Backend_C Testcode"
CompilerEndIf
CompilerElse
CompilerError "Please use PB 6.00 Beta 1."
CompilerEndIf
ProcedureReturn w
EnableDebugger
EndProcedure
x.i = $FF000000
Debug RSet(Hex(x),16,"0")
x = bswap(x,y)
Debug RSet(Hex(x),16,"0")
; 0000000140001090 | 55 | push rbp |
; 0000000140001091 | 48:89E5 | mov rbp,rsp |
; 0000000140001094 | 48:83EC 10 | sub rsp,10 |
; 0000000140001098 | 894D 10 | mov dword ptr ss:[rbp+10],ecx |
; 000000014000109B | 8955 18 | mov dword ptr ss:[rbp+18],edx |
; 000000014000109E | C745 FC 00000000 | mov dword ptr ss:[rbp-4],0 |
; 00000001400010A5 | C745 F8 00000000 | mov dword ptr ss:[rbp-8],0 |
; 00000001400010AC | 8B45 10 | mov eax,dword ptr ss:[rbp+10] |
; 00000001400010AF | 8945 F8 | mov dword ptr ss:[rbp-8],eax |
; 00000001400010B2 | 8B45 F8 | mov eax,dword ptr ss:[rbp-8] |
; 00000001400010B5 | 0FC8 | bswap eax |
; 00000001400010B7 | 8945 18 | mov dword ptr ss:[rbp+18],eax |
; 00000001400010BA | 8B45 18 | mov eax,dword ptr ss:[rbp+18] |
; 00000001400010BD | 8945 FC | mov dword ptr ss:[rbp-4],eax |
; 00000001400010C0 | 90 | nop |
; 00000001400010C1 | 8B45 FC | mov eax,dword ptr ss:[rbp-4] |
; 00000001400010C4 | 48:83C4 10 | add rsp,10 |
; 00000001400010C8 | 5D | pop rbp |
; 00000001400010C9 | C3 | ret |
Re: inline asm c back end
Posted: Sat Dec 04, 2021 5:16 am
by juergenkulow
Re: inline asm c back end
Posted: Sat Dec 04, 2021 10:29 pm
by idle
Here's a good explanation of using asm templates.
https://www.felixcloutier.com/documents/gcc-asm.html
statement structure boils down to this.
asm <optional stuff> (
"assembler template"
: outputs
: inputs
: clobbers
: labels)
I've come up with these macros to make it a little easier to use intel syntax. Yes we need to modify our asm a little and follow a pattern returning through a variable rather than expecting to have a return on eax/rax
Procedure shift_right(v.l, shift.l)
Protected ret.l
BeginAsm()
!"mov eax,%[v];"
!"mov ecx,%[shift];"
!"shr eax,cl;"
!"mov %[ret],eax;"
AsmOutput(ret)
AsmInput2(v,shift)
EndAsm()
Test code
Code: Select all
Macro AsmInput(var)
!:[var] "r" (v_#var)
EndMacro
Macro AsmInput2(var0,var1)
!:[var0] "r" (v_#var0),[var1] "r" (v_#var1)
EndMacro
Macro AsmInput3(var0,var1,var2)
!:[var0] "r" (v_#var0),[var1] "r" (v_#var1),[var2] "r" (v_#var2)
EndMacro
Macro AsmOutput(var)
!".att_syntax;"
!:[var] "=r" (v_#var)
EndMacro
Macro BeginAsm()
!__asm__(
!".intel_syntax noprefix;"
EndMacro
Macro BeginAsmGoto()
!asm goto(
!".intel_syntax noprefix;"
EndMacro
Macro EndAsm()
!);
EndMacro
Procedure bswap(v.l)
Protected ret.l = 0
CompilerIf #PB_Compiler_Backend = #PB_Backend_C
BeginAsm()
!"mov eax,%[v];"
!"bswap eax;"
!"mov %[ret], eax;"
AsmOutput(ret)
AsmInput(v)
EndAsm()
CompilerElse
!mov eax, [p.v_v]
!bswap eax
!mov [p.v_ret],eax
CompilerEndIf
ProcedureReturn ret
EndProcedure
Procedure shift_right(v.l, shift.l)
Protected ret.l =0
CompilerIf #PB_Compiler_Backend = #PB_Backend_C
BeginAsm()
!"mov eax,%[v];"
!"mov ecx,%[shift];"
!"shr eax,cl;"
!"mov %[ret],eax;"
AsmOutput(ret)
AsmInput2(v,shift)
EndAsm()
CompilerElse
!mov eax, [p.v_v]
!mov ecx, [p.v_shift]
!shr eax, cl
!mov [p.v_ret],eax
CompilerEndIf
ProcedureReturn ret
EndProcedure
Procedure absint(a.i)
Protected ret.i
CompilerIf #PB_Compiler_Backend = #PB_Backend_C
BeginAsm()
!"mov rdx,%[a];"
!"mov rax, rdx;"
!"neg rax;"
!"CMOVl rax,rdx;"
!"mov %[ret],rax;"
AsmOutput(ret)
AsmInput(a)
EndAsm()
CompilerElse
!mov rdx,[p.v_a]
!mov rax, rdx
!neg rax
!CMOVl rax,rdx
!mov [p.v_ret],rax
CompilerEndIf
ProcedureReturn ret
EndProcedure
;-tests
x.i = $FF000000
Debug RSet(Hex(x),8,"0")
x= bswap(x)
Debug RSet(Hex(x),8,"0")
x = absint(-42)
Debug x
xx.l = $FF000000
Debug RSet(Bin(xx, #PB_Long),32,"0")
xx = shift_right(xx, 1)
Debug RSet(Bin(xx, #PB_Long),32,"0")
Re: inline asm c back end
Posted: Sun Dec 05, 2021 11:53 am
by Lord
Are these results correct?
Code: Select all
Macro AsmInput(var)
!:[var] "r" (v_#var)
EndMacro
Macro AsmInput2(var0,var1)
!:[var0] "r" (v_#var0),[var1] "r" (v_#var1)
EndMacro
Macro AsmInput3(var0,var1,var2)
!:[var0] "r" (v_#var0),[var1] "r" (v_#var1),[var2] "r" (v_#var2)
EndMacro
Macro AsmOutput(var)
!".att_syntax;"
!:[var] "=r" (v_#var)
EndMacro
Macro BeginAsm()
!__asm__(
!".intel_syntax noprefix;"
EndMacro
Macro BeginAsmGoto()
!asm goto(
!".intel_syntax noprefix;"
EndMacro
Macro EndAsm()
!);
EndMacro
Procedure bswap(v.l)
Protected ret.l = 0
CompilerIf #PB_Compiler_Backend = #PB_Backend_C
BeginAsm()
!"mov eax,%[v];"
!"bswap eax;"
!"mov %[ret], eax;"
AsmOutput(ret)
AsmInput(v)
EndAsm()
CompilerElse
!mov eax, [p.v_v]
!bswap eax
!mov [p.v_ret],eax
CompilerEndIf
ProcedureReturn ret
EndProcedure
Procedure shift_right(v.l, shift.l)
Protected ret.l =0
CompilerIf #PB_Compiler_Backend = #PB_Backend_C
BeginAsm()
!"mov eax,%[v];"
!"mov ecx,%[shift];"
!"shr eax,cl;"
!"mov %[ret],eax;"
AsmOutput(ret)
AsmInput2(v,shift)
EndAsm()
CompilerElse
!mov eax, [p.v_v]
!mov ecx, [p.v_shift]
!shr eax, cl
!mov [p.v_ret],eax
CompilerEndIf
ProcedureReturn ret
EndProcedure
Procedure absint(a.i)
Protected ret.i
CompilerIf #PB_Compiler_Backend = #PB_Backend_C
BeginAsm()
!"mov rdx,%[a];"
!"mov rax, rdx;"
!"neg rax;"
!"CMOVl rax,rdx;"
!"mov %[ret],rax;"
AsmOutput(ret)
AsmInput(a)
EndAsm()
CompilerElse
!mov rdx,[p.v_a]
!mov rax, rdx
!neg rax
!CMOVl rax,rdx
!mov [p.v_ret],rax
CompilerEndIf
ProcedureReturn ret
EndProcedure
Procedure Tests(x)
Debug RSet(Hex(x),8,"0")
Debug RSet(Hex(absint(x)),8,"0")
b.s=RSet(Bin(x), 64, "0")
Debug Left(b, 16)+" "+Mid(b, 17, 16)+" "+Mid(b, 33, 16)+" "+Right(b, 16)
x= bswap(x)
Debug RSet(Hex(x),8,"0")
Debug RSet(Hex(absint(x)),8,"0")
b.s=RSet(Bin(x), 64, "0")
Debug Left(b, 16)+" "+Mid(b, 17, 16)+" "+Mid(b, 33, 16)+" "+Right(b, 16)
Debug "----"
EndProcedure
;-tests
x.i = $FF000000
Tests(x)
x.i = $00FF0000
Tests(x)
x.i = $0000FF00
Tests(x)
x.i = $000000FF
Tests(x)
x.i = $DEADBEEF
Tests(x)
x.i = $DEAD0000
Tests(x)
x.i = $0000BEEF
Tests(x)
Re: inline asm c back end
Posted: Sun Dec 05, 2021 8:34 pm
by idle
looks ok to me, the routines aren't of any significance as such, whats important is that the outputs are the same between fasm and gcc. The objective is to find a methodology which requires the least adaptation of existing asm. This is what I've got so far and I think its a fair compromise and easy enough to use, without much gnashing of teeth.
These are the results from my previous post
[idel@idle-pc PB]$ pbcompilerc -d junk6.pb
PureBasic 6.00 Beta 1 - C Backend (Linux - x64)
[Debugger] FF000000
[Debugger] 000000FF
[Debugger] 42
[Debugger] 11111111000000000000000000000000
[Debugger] 01111111100000000000000000000000
[idle@idle-pc PB]$ pbcompiler -d junk6.pb
PureBasic 6.00 Beta 1 (Linux - x64)
[Debugger] FF000000
[Debugger] 000000FF
[Debugger] 42
[Debugger] 11111111000000000000000000000000
[Debugger] 01111111100000000000000000000000
Re: inline asm c back end
Posted: Mon Dec 06, 2021 6:08 am
by juergenkulow
Code: Select all
; bswap without Macros
Procedure bswap(v.l)
Protected ret.l
CompilerIf #PB_Compiler_Backend = #PB_Backend_C
CompilerIf #PB_Processor_x64=#PB_Compiler_Processor Or #PB_Processor_x86=#PB_Compiler_Processor
!__asm__(".intel_syntax noprefix;"
!"mov eax,%[v];"
!"bswap eax;"
!"mov %[ret], eax;"
!".att_syntax prefix"
!:[ret] "=r" (v_ret) : [v] "r" (v_v) );
CompilerElseIf #PB_Processor_Arm64=#PB_Compiler_Processor
CompilerError "No Arm64, M1 Code"
CompilerElseIf #PB_Processor_Arm32=#PB_Compiler_Processor
CompilerError "No Arm32 Code"
CompilerEndIf
CompilerElse
CompilerError "C Backend Testcode only."
CompilerEndIf
ProcedureReturn ret
EndProcedure
;-tests
x.i = $FF00EE11
s.s=RSet(Hex(x,#PB_Long),8,"0")+" "
x= bswap(x)
; ! v_x=__builtin_bswap32(v_x); // With /OPTIMIZER on Windows x64 is one mov eax,eax faster.
s+RSet(Hex(x,#PB_Long),8,"0")
Debug s
SetClipboardText(s)
; FF00EE11 11EE00FF
Re: inline asm c back end
Posted: Mon Dec 06, 2021 9:56 pm
by idle
Thanks I will have to make a template out of the compiler if's. I doubt you'd notice any speed difference with just one additional mov and using the builtins will come in very useful to