code: 9ferno

Download patch

ref: 4f27e3f42fb2cfd9ef449ddc0aa01e6795dcb68c
parent: 8f8d569ccada3dd7c29cc8562ce7c5e2923d89d1
author: 9ferno <gophone2015@gmail.com>
date: Thu Dec 16 06:17:00 EST 2021

tests to show the asm calling conventions

--- a/emu/port/devdynld.c
+++ b/emu/port/devdynld.c
@@ -4,7 +4,7 @@
 #include	<a.out.h>
 #include	<dynld.h>
 
-#define	DBG	if(1) print
+static int debug = 0;
 
 extern ulong ndevs;
 
--- a/os/port/devforth.c
+++ b/os/port/devforth.c
@@ -11,12 +11,10 @@
 extern Fentry fentries[];
 
 /*
- * 1. Provides #f/forth/new to start new forth processes
- * 2. pipe data between the readers and writers of #f/forth/pid/(stdin stdout stderr)
+ 1. Provides #f/forth/new to start new forth processes
+ 2. pipe data between the readers and writers of #f/forth/pid/(stdin stdout stderr)
 		do not do this. too much work. use the existing mechanism.
 		use the parent's Fgrp - easy, simple and it works fine
-	TODO
-	add memory, variables, dictionary, return stack, parameter stack
  */
 enum
 {
@@ -90,16 +88,16 @@
 		f = &fentries[i];
 		if(f->type == Header){
 			*(intptr*)h = (intptr)dtop;
-			print("Header 0x%zx: 0x%zx 0x%zx ", h, *(intptr*)h, dtop);
+			DBG("Header 0x%zx: 0x%zx 0x%zx ", h, *(intptr*)h, dtop);
 			dtop = h;
 			h += sizeof(intptr);
 			*h = f->hdr.len;
-			print("len 0x%zx: 0x%d ", h, *h);
+			DBG("len 0x%zx: 0x%d ", h, *h);
 			h++;
 			strncpy((s8*)h, f->hdr.name, f->hdr.len);
-			print("name 0x%zx: ", h);
+			DBG("name 0x%zx: ", h);
 			for(n = 0; n < f->hdr.len; n++){
-				print("%c", *(h+n));
+				DBG("%c", *(h+n));
 			}
 			h += f->hdr.len;
 			if((f->hdr.len+1)%8 > 0){
@@ -106,20 +104,20 @@
 				h += 8-((f->hdr.len+1)%8);
 			}
 			*(intptr*)h = (intptr)f->hdr.cfa;
-			print(" cfa 0x%zx: 0x%zx 0x%zx\n", h, *(intptr*)h, (intptr)f->hdr.cfa);
+			DBG(" cfa 0x%zx: 0x%zx 0x%zx\n", h, *(intptr*)h, (intptr)f->hdr.cfa);
 			h += sizeof(intptr);
 		}else if(f->type == IHeader){
 			*(intptr*)h = (intptr)dtop;
-			print("IHeader 0x%zx: 0x%zx 0x%zx ", h, *(intptr*)h, dtop);
+			DBG("IHeader 0x%zx: 0x%zx 0x%zx ", h, *(intptr*)h, dtop);
 			dtop = h;
 			h += sizeof(intptr);
 			*h = f->hdr.len | (1<<7);
-			print("len 0x%zx: 0x%d ", h, *h);
+			DBG("len 0x%zx: 0x%d ", h, *h);
 			h++;
 			strncpy((s8*)h, f->hdr.name, f->hdr.len);
-			print("name 0x%zx: ", h);
+			DBG("name 0x%zx: ", h);
 			for(n = 0; n < f->hdr.len; n++){
-				print("%c", *(h+n));
+				DBG("%c", *(h+n));
 			}
 			h += f->hdr.len;
 			if((f->hdr.len+1)%8 > 0){
@@ -126,15 +124,15 @@
 				h += 8-((f->hdr.len+1)%8);
 			}
 			*(intptr*)h = (intptr)f->hdr.cfa;
-			print(" cfa 0x%zx: 0x%zx 0x%zx\n", h, *(intptr*)h, (intptr)f->hdr.cfa);
+			DBG(" cfa 0x%zx: 0x%zx 0x%zx\n", h, *(intptr*)h, (intptr)f->hdr.cfa);
 			h += sizeof(intptr);
 		}else if(f->type == Absolute){
 			*(intptr*)h = f->p;
-			print("	0x%zx: 0x%zx 0x%zx\n", h, *(intptr*)h, (intptr)f->p);
+			DBG("	0x%zx: 0x%zx 0x%zx\n", h, *(intptr*)h, (intptr)f->p);
 			h += sizeof(intptr);
 		}else if(f->type == FromH0){
 			*(intptr*)h = (intptr)fmem+DICTIONARY+f->p;
-			print("	0x%zx: 0x%zx 0x%zx\n", h, *(intptr*)h, (intptr)fmem+DICTIONARY+f->p);
+			DBG("	0x%zx: 0x%zx 0x%zx\n", h, *(intptr*)h, (intptr)fmem+DICTIONARY+f->p);
 			h += sizeof(intptr);
 		}else if(f->type == Chars){
 			strcpy((s8*)h, f->str);
@@ -146,7 +144,7 @@
 	}
 	*(intptr*)(fmem + HERE) = (intptr)h;
 	*(intptr*)(fmem + DTOP) = (intptr)dtop;
-	print("loadforthdictionary fmem 0x%zx h 0x%zx dtop 0x%zx (intptr*)(fmem + DTOP) 0x%zx *(intptr*)(fmem + DTOP) 0x%zx\n",
+	DBG("loadforthdictionary fmem 0x%zx h 0x%zx dtop 0x%zx (intptr*)(fmem + DTOP) 0x%zx *(intptr*)(fmem + DTOP) 0x%zx\n",
 		fmem, (intptr)h, (intptr)dtop, (intptr*)(fmem + DTOP), *(intptr*)(fmem + DTOP));
 }
 
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -1,4 +1,3 @@
 *.dis
 *.out
 6.*
-*.s
--- /dev/null
+++ b/tests/asmconventions.c
@@ -1,0 +1,414 @@
+#include <u.h>
+#include <libc.h>
+
+extern void asmarg1(int);
+extern void asmarg2(int);
+
+/*
+check the generated asm and the calling conventions. use 6c -S asmconventions.c
+check the inferno.notes for the explanation
+*/
+
+s64
+arg0(void)
+{
+	return 8+4;
+}
+
+s64
+arg1(s64 a)
+{
+	return a+4;
+}
+
+s64
+arg2(s64 a, s64 b)
+{
+	return a+arg1(a)+b;
+}
+
+s64
+arg3(s64 a, s64 b, s64 c)
+{
+	return a+b+c;
+}
+
+s64
+arg3caller(s64 a, s64 b, s64 c)
+{
+	return arg3(a+10,b+20,c+30);
+}
+
+s64
+arg2local2(s64 a, s64 b)
+{
+	s64 c = 10, d = 20;
+
+	c = arg1(a);
+	d = c+arg1(b)+a;
+	return d;
+}
+
+s64
+arg2local2s(s64 a, s64 b)
+{
+	s64 c = 10, d = 20;
+
+	c += arg2(a,d);
+	d = c+a+2;
+	return d;
+}
+
+s64
+arg2local6s(s64 a, s64 b)
+{
+	s64 c = 10, d = 20, e = 30, f = 40, g = 50, h = 60;
+
+	c = arg2(a,d);
+	d = c+a+2+f+g+h;
+	return d;
+}
+
+s64
+arg3local1(s64 a, s64 b, s64 c)
+{
+	int d = 50;
+
+	return arg3(a+10,b+20,c+30+d+10);
+}
+
+s64
+arg3local2(s64 a, s64 b, s64 c)
+{
+	int d = 50, e = 60;
+
+	return arg3(a+10,b+20+e+10,c+30+d+10);
+}
+
+void
+main(int, void**)
+{
+	arg3local2(1,2,3);
+	exits("");
+}
+
+/*
+
+teaching C programming language
+<cinap_lenrek> c is very simple language  [18:01]
+<cinap_lenrek> Glats: this just takes practice.  [18:02]
+<cinap_lenrek> Glats: how many languages can you read?
+<Glats> java, golang, js, ruby , php and a little c++  [18:03]
+<cinap_lenrek> Glats: so you shouldnt have much difficulty reading c  [18:05]
+<cinap_lenrek> Glats: anything specific you have difficulty with in c?  [18:06]
+<Glats> now i dont understand the folder structure
+<Glats> and where is the "main()"  [18:07]
+<cinap_lenrek> folder struture?
+<Glats> sorry
+<cinap_lenrek> you mean modules?
+<Glats> structure  [18:08]
+<Glats> yeah
+<cinap_lenrek> or you mean the struct data types?
+<Glats> and where start the program i mean the main function
+<cinap_lenrek> ok
+<cinap_lenrek> so thats a bit tautological  [18:09]
+<cinap_lenrek> a c program consists just of a bunch of functions
+<cinap_lenrek> and the main function is just the one where execution starts
+<cinap_lenrek> when one program does a exec() the os replaces the text/data segment of the process with the new one and jumps to the new programs main function, passing the arguments  [18:10]
+<cinap_lenrek> its mostly a convention
+<cinap_lenrek> not really language specific
+<cinap_lenrek> when you look close
+<cinap_lenrek> libc has a bunch of assembly that sets up a stack and then manually jumps to main()  [18:11]
+<cinap_lenrek> a c compiler only knows how to compile functions
+<cinap_lenrek> and whats a function?
+<cinap_lenrek> its also a convention
+<cinap_lenrek> its some hunk of code with a entry point and a calling convention on how to pass it parameters  [18:12]
+<cinap_lenrek> and a way to return from the function
+<cinap_lenrek> a function can be written in other languages, in a program
+<cinap_lenrek> like assembly
+<cinap_lenrek> or be implemented as a system call  [18:13]
+<cinap_lenrek> anyway
+<cinap_lenrek> you run the c compiler on a c source file
+<cinap_lenrek> and it generates these hunks of code
+<cinap_lenrek> and a symbol table
+<cinap_lenrek> and then you use a linker to cat all these hunks together, and make all the addresses absolute  [18:14]
+<cinap_lenrek> and you get one huge hunk of code with a entry point, which is the main... or some assembly that then jumps to the main
+<cinap_lenrek> the linker also figures out which functions are needed  [18:15]
+<cinap_lenrek> and can leave out .o files that it doesnt need
+<cinap_lenrek> thats why libraries often have one function per .c file
+<cinap_lenrek> so if you dont use the function, the linker can omit the object code from the resulting binary
+<cinap_lenrek> does this make sense?  [18:17]
+<Glats> of course
+<Glats> perfectly
+<cinap_lenrek> experiment
+<cinap_lenrek> B /tmp/a.c
+<joe9> cinap_lenrek: that is the most concise and accurate description of C.  [18:18]
+<cinap_lenrek> int
+<cinap_lenrek> add(int a, int b)
+<cinap_lenrek> {
+<cinap_lenrek> return a+b;
+<cinap_lenrek> }
+<cinap_lenrek> very simple function
+<cinap_lenrek> 6c -S a.c
+<cinap_lenrek> term% 6c -S a.c
+<cinap_lenrek> TEXT add+0(SB),0,$0
+<cinap_lenrek> MOVL BP,AX
+<cinap_lenrek> ADDL b+8(FP),AX
+<cinap_lenrek> RET ,
+<cinap_lenrek> thats what the c compiler produces  [18:19]
+<cinap_lenrek> the "int a" parameter is passed in BP register here
+<cinap_lenrek> thats just the calling convention of amd64
+<cinap_lenrek> the first arg is passed in BP register
+<cinap_lenrek> (on plan9
+<cinap_lenrek> and b+8(FP) is the second parameter "int b", from the stack  [18:20]
+<cinap_lenrek> return value is passed in AX register
+<cinap_lenrek> int
+<cinap_lenrek> sub(int a, int b)
+<cinap_lenrek> {
+<cinap_lenrek> return a-b;
+<cinap_lenrek> }
+<cinap_lenrek> put a second function there, repeat
+<cinap_lenrek> and it just prints two functions in assembly  [18:21]
+<cinap_lenrek> note, theres no main() in this file
+<cinap_lenrek> but it compile everything just fine  [18:22]
+<cinap_lenrek> the -S flag just make it print assembly
+<cinap_lenrek> now, lets try to link it
+<cinap_lenrek> term% 6l a.6
+<cinap_lenrek> _main: undefined: main in _main
+<cinap_lenrek> crazy!
+<cinap_lenrek> whats _main?
+<cinap_lenrek> thats the assembly function i told you before  [18:23]
+<cinap_lenrek> that sets up the stack and then calls main()
+<cinap_lenrek> but here, the linker cannot find a main() function in the symbol table because he havnt made one
+<cinap_lenrek> you could make a second .c file
+<cinap_lenrek> b.c
+<cinap_lenrek> and have a main() function in there
+<cinap_lenrek> and then it would be able to link a.6 and b.6 together and that would work  [18:24]
+<cinap_lenrek> anyway
+<cinap_lenrek> if you call a functoin in c, for the compiler, its mostly just a name  [18:25]
+<cinap_lenrek> (and a function signature (parameter types))
+<cinap_lenrek> later, the linker resolves this name
+<cinap_lenrek> everything gets resolved at link time  [18:26]
+<Glats> pretty cool
+<Glats> thnks you so much
+<cinap_lenrek> you know function "prototypes"?
+<cinap_lenrek> the stuff in the .h files?
+<Glats> nope :/
+<cinap_lenrek> thats basically to tell the compiler which arguments and types are there  [18:27]
+<qeed> just read a c book man
+<Glats> and you can import it?
+<Glats> to the .c files right?
+<cinap_lenrek> its just so the compiler can warn you when you pass the wrong types
+<cinap_lenrek> its just a promise, that later, at link time, that function will be there  [18:28]
+<cinap_lenrek> the linker only cares about functions that get actually *USED*
+<Glats> like a signature?  [18:29]
+<cinap_lenrek> there are signatures involved
+<cinap_lenrek> so that what the c compiler thinks the parameter types are and whats in already compiled object files matches  [18:30]
+<Glats> ok
+<cinap_lenrek> tho thats plan9 specific
+<cinap_lenrek> i mean, c doesnt mandate this
+<cinap_lenrek> it could as well just assume everything matches always  [18:31]
+<cinap_lenrek> theres just one namespace
+<cinap_lenrek> someone calls add(1,2);  [18:32]
+<cinap_lenrek> compiler puts CALL add(SB) in the object file
+<cinap_lenrek> linker just looks for a symbol called "add" in all the object files
+<cinap_lenrek> and then produces the *actual* machine code with the name replaced by its memory address
+<Glats> nice  [18:35]
+<cinap_lenrek> note
+<Glats> i got you
+<cinap_lenrek> this isnt really c :)
+<cinap_lenrek> more like how the plan9 (and many other) c compilers/linkers work
+<cinap_lenrek> you could as well make a c compiler that only uses single .c files  [18:36]
+<cinap_lenrek> and has no support for modules
+<cinap_lenrek> or a interpreter
+<cinap_lenrek> or one that dynamically compiles c files
+<Glats> thank you again. and i hope not bother you. I will read more code  [18:37]
+<cinap_lenrek> another hint  [18:38]
+<cinap_lenrek> .a files are basically just zip files with many .o files in them
+<cinap_lenrek> (without the compression)
+<cinap_lenrek> ar(6)
+<Glats> nice  [18:40]
+
+19:03 < joe7> Text callfunction(SB), 0, $-4 or $0 or whatever has nothing to do with the contents of the stack. It is used to set the SP location in the callee.
+19:03 < joe7> does that make sense?
+19:11 < ori> depends on what you mean by 'has nothign to do with the contents of the stack'
+19:11 < ori> it says how much stack space callfuntion() is going to use.
+19:11 < ori> which is the same as the value of SP in the callee
+19:12 < joe7> if we do a call callfunction(SB), the stack still has the return pc (the pc after the call)?
+19:12 < joe7> I am trying to figure out how setlabel in l.s works.
+19:13 < joe7> I see the procswitch() calls setlabel()
+19:13 < joe7> but, I am not able to visualize the stack contents as 0(SP) is the return pc in setlabel().
+19:14 < joe7> that return pc is the instruction after the if(!setlabel(&up->sched).. call?
+19:14 < joe7> or the is return pc the pc after the caller of procswitch()?
+19:17 < joe7> MOVQ0(SP), BX -- store return PC 
+19:17 < joe7> so 0(SP) is the return PC.
+19:18 < joe7> I am trying to figure out what return PC is?
+19:18 < joe7> the instruction after call setlabel()? or, the instruction after procswitch()?
+19:55 < ori> the instruction after the function call.
+19:56 < ori> this is just x86: read the docs for the call instruction
+20:09 < joe7> What did the $-4 do then? SP would be the same with $-4 or $0, correct?
+21:06 < ori> joe7: dunnoo.
+21:08 < ori> reading /sys/doc/asm.ps, it says 'On machines with a link register, such as MIPS and SPARC, the special value -4 instructs the loader to genreate no PC save and restore instructions, even if the function is not a leaf.'
+21:23 < joe7> so, on amd64, -4 does nothing?
+21:27 < ori> dunno, assemble it and see?
+21:27 < ori> *disassemble it and see?
+21:29 < joe7> what tool do you use for disassembling?
+21:35 < ori> acid
+21:41 < ori> asm(func)
+21:43 < joe7> isn't that what 6c -S does?
+21:44 < joe7> It does not show how the relationship between SP and return PC.
+21:50 < joe7> http://okturing.com/src/12786/body the first local adds 16 bytes to that number and the second local does not change the number.
+22:02 < ori> joe7: no.
+22:02 < ori> 6c -s obviously shows the asm before the linker does anything with it.
+22:02 < ori> asm sees it after.
+Day changed to 14 Dec 2021
+02:43 < cinap_lenrek> joe7: 0(SP) is return pc, yes
+02:44 < k0ga> cinap_lenrek: hi!
+02:44 < k0ga> cinap_lenrek: I already have the house
+02:44 < cinap_lenrek> joe7: and the $-4 offset in the TEXT is a flag
+02:44 < cinap_lenrek> its not a real offset
+02:44 < cinap_lenrek> k0ga: WOOOO!!!!
+02:44 < k0ga> I told quinq yesterday that maybe it is a good idea if you guys go there this weekend
+02:46 < cinap_lenrek> k0ga: yeah, cool
+02:48 < cinap_lenrek> joe7: also note, the stack on most archs need to be aligned!
+02:48 < k0ga> cinap_lenrek: talk with quinq and fer about it please ;)
+02:48 < cinap_lenrek> k0ga: okay! :)
+02:49 < k0ga> 10:48 < cinap_lenrek> joe7: also note, the stack on most archs need to be aligned! <- and in the few were the arch doesn't need it the compiler needs it
+02:50 < cinap_lenrek> joe7: and 4 isnt aligned on 64 bit arch... needs to be 8 byte aligned...
+02:50 < cinap_lenrek> or even 16 with elf abi
+02:50 < k0ga> cinap_lenrek: hahahahahahah
+02:50 < cinap_lenrek> because intel screwed up their mmx
+
+05:55 < joe7> 02:44 < cinap_lenrek> joe7: and the $-4 offset in the TEXT is a flag -- I am trying to figure out what the flag does on amd64?
+05:55 < joe7> do not reserve any memory for the locals?
+05:56 < joe7> Isn't that what $0 would do?
+05:56 < joe7> too.
+05:56 < joe7> So, how is $-4 different on amd64?
+
+11:39 < joe7> ori, cinap_lenrek the src() and asm() show totally different things (I think). Just want to check if this makes sense to you http://okturing.com/src/12788/body
+11:40 < joe7> I am not even sure if asm() is showing the same code as src()
+11:41 < Leimy> khm: Face McShooty... that's all I have to say :-)
+11:42 < piroko> khm: oh man I definitely lost it at ?bonerfarts?
+11:42 < piroko> my sense of humor has never been called mature though
+11:43 < Leimy> oh it's juevenile for sure... and that's part of why I like it.
+12:11 < cinap_lenrek> jor7: off
+12:11 < cinap_lenrek> jor7: no idea whats going on there
+12:12 < cinap_lenrek> works fine with our kernel
+12:12 < joe7> the code is not 9front code. but, 9ferno's. ok, will try with teh 9front kernel.
+12:12 < joe7> thanks.
+12:13 < joe7> cinap_lenrek: I am reading the mailing list to find answers to my questions. I found this by you: 
+              https://marc.info/?l=9fans&m=145436696708016&w=2
+12:13 < cinap_lenrek> tho theres a odd thing that it is one line ahead
+12:13 < joe7> but, 0(FP) would be the return PC, correct?
+12:13 < cinap_lenrek> maybe that is the issue
+12:14 < joe7> I understand that technically 0(FP) should be the first argument.
+12:14 < cinap_lenrek> and the assembler generates off by one line numbers in the debug info
+12:14 < joe7> is there a better way to use asm() in acid to see the assembly of a function?
+12:15 < joe7> or, is it asm(), casm()...
+12:15 < cinap_lenrek> joe7 ? but, 0(FP) would be the return PC, correct?
+12:15 < cinap_lenrek> no
+12:15 < cinap_lenrek> i dont think so
+12:16 < cinap_lenrek> 0(SP) is
+12:16 < cinap_lenrek> i mean
+12:16 < cinap_lenrek> lets try it
+12:16 < joe7> http://okturing.com/src/12789/body is how I understand the SP and FP relationship.
+12:16 < cinap_lenrek> TEXT _main(SB), $-4
+12:16 < cinap_lenrek> MOV 0(FP), AX
+12:17 < joe7> another thing I could not understand is why do you use $-4 for assembly? It does not make a difference in amd64, correct?
+12:17 < joe7> Why not just $0 then?
+12:18 < joe7> I understand that it is a flag. But, I cannot figure out what the difference between $-4 and $0 is (on amd64).
+12:19 < quinq> k0ga, sure, when?
+12:21 < cinap_lenrek> acid: asm(_main)
+12:21 < cinap_lenrek> _main 0x0000000000010028 MOV 8(SP),R0
+12:21 < cinap_lenrek> _main+0x4 0x000000000001002c RETURN
+12:21 < cinap_lenrek> acid: asm(_main)
+12:21 < cinap_lenrek> _main 0x0000000000010028 MOV 8(SP),R0
+12:21 < cinap_lenrek> _main+0x4 0x000000000001002c RETURN
+12:21 < cinap_lenrek> TEXT _main(SB), $0
+12:21 < cinap_lenrek> MOV 0(FP), R0
+12:21 < cinap_lenrek> RETURN
+12:21 < cinap_lenrek> so no.
+12:21 < cinap_lenrek> 0(FP) is the first arg
+12:23 < joe7> My mistake is that I am looking at SP and FP as stack pointers. I probably should be looking at them as data structures. Where x(FP) refers to argument x(?)
+12:23 < joe7> I understand that they translate to stack pointers.
+12:23 < cinap_lenrek> its not a real register
+12:24 < cinap_lenrek> its an offset of the stack pointer
+12:24 < joe7> is there space in the stack for 0(FP)?
+12:25 < joe7>  its an offset of the stack pointer -- it tends to be so for the 2nd and so on arguments.
+12:25 < joe7> but, thinking about FP as an offset to the stack pointer falls down when we consider 0(FP), correct?
+12:25 < cinap_lenrek> why?
+12:26 < joe7> Is there space in the stack for 0(FP)?
+12:26 < cinap_lenrek> yes, theres a slot reserved for 0(FP) as well
+12:26 < cinap_lenrek> even if it is passed as a register
+12:26 < joe7> http://okturing.com/src/12789/body in this example, then FP = address 0?
+12:26 < cinap_lenrek> in case we need to save it, we can put it back on the stack
+12:27 < joe7> so, 0(SP) is return pc. 8(SP) is the callee's second argument?
+12:27 < joe7> 0(SP) is my (caller) return pc
+12:27 < cinap_lenrek> return pc is in 0(FP)
+12:27 < cinap_lenrek> 1st argument is in BP
+12:27 < cinap_lenrek> 2nd argument is at 8(FP)
+12:28 < cinap_lenrek> look, this cant both be true
+12:28 < cinap_lenrek> 1st argument is *PASSED* in BP, but 0(FP) should be valid as well
+12:28 < joe7> so, what is the x(SP) for the first argument?
+12:28 < cinap_lenrek> 8(SP)
+12:28 < joe7> it is not 8(SP) as that woud be the second argument.
+12:29 < cinap_lenrek> how?
+12:30 < joe7> http://okturing.com/src/12790/body
+12:30 < joe7>         ADDQ    $20,DI
+12:30 < joe7>         MOVQ    DI,8(SP)
+12:30 < joe7> is for the second argument
+12:31 < joe7> We know that 0(SP) is the return pc already.
+12:31 < joe7> so, there is no x(SP) for the first argument
+12:32 < cinap_lenrek> thats the compiler output
+12:32 < cinap_lenrek> now link it and disassemble it
+12:33 < cinap_lenrek> it will emit code where it moves the stack pointer right after entry to allocate space for the arguments for the call arg3()
+12:33 < cinap_lenrek> then FP accesses take this into account
+12:33 < cinap_lenrek> basically, the whole idea for FP is that it is always pointing to YOUR call frame arguments
+12:33 < joe7> ok, thanks. will check it out. BTW, if I am writing asm, I should assume that 0(SP) = return pc, 8(SP) = second argument, correct?
+12:34 < cinap_lenrek> no matter what other local variables you allocated
+12:34 < joe7> yes, what you say about FP and SP as stack pointers might make sense after the linker magic.
+12:34 < cinap_lenrek> 8(SP) is the slot of the first argument
+12:34 < cinap_lenrek> 16(SP) is the second argument
+12:34 < cinap_lenrek> note, on *ENTRY*
+12:35 < cinap_lenrek> the caller doesnt know how much local variables a function will use
+12:35 < joe7> are you talking after the linker, disassembly?
+12:35 < cinap_lenrek> the function itself will have a prolog to move the stack pointer by the amount needed for local vars
+12:35 < cinap_lenrek> what?
+12:36 < cinap_lenrek> after the linker, FP is gone
+12:36 < cinap_lenrek> its like a handy macro
+12:36 < cinap_lenrek> in the actual machine code it will reference only SP
+12:36 < joe7> before the linker, this is wrong. < cinap_lenrek> 8(SP) is the slot of the first argument
+12:36 < joe7> it is referring to the second argument.
+12:38 < cinap_lenrek> joe7: maybe i'm confused, but then you just show me where this is the case
+12:38 < joe7> http://okturing.com/src/12790/body
+12:39 < joe7> b+20 is to 8(SP)
+12:39 < cinap_lenrek> no
+12:39 < cinap_lenrek> thats the new callframe!
+12:39 < cinap_lenrek> thats the callframe for arg3() call
+12:39 < cinap_lenrek> and you have to remember that the CALL also *PUSHES* the returnpc
+12:40 < Leimy> Thank Wheeler... stupid subroutines
+12:40 < joe7> I am confused now.
+12:40 < joe7> in arg3caller, SP is the stack pointer of this function. FP is the call frame of this function.
+12:41 < joe7> when arg3() is entered, the FP there would be a stack pointer somewhere related to the SP of the caller, correct?
+12:41 < cinap_lenrek> this is x86
+12:41 < cinap_lenrek> a call will PUSH the returnpc on the stack
+12:41 < cinap_lenrek> theres no link register like on risc
+12:41 < cinap_lenrek> so on entry, the world looks different
+12:42 < joe7> the FP in the callee 
+12:42 < cinap_lenrek> yes
+12:42 < joe7> would be somehow related to the SP of the caler, correct?
+12:42 < cinap_lenrek> wtf are you talking about?
+12:42 < joe7> for example, 8(SP) in caller == 0(FP) in callee?
+12:43 < joe7> assuming that is the first argument.
+12:43 < cinap_lenrek> how often do i need to repeat that CALL instruction subtracts 8 from the stack pointer?
+12:44 < joe7> that is what I do not understand.
+12:44 < sigrid> joe7: https://www.felixcloutier.com/x86/call
+12:44 < sigrid> (great read)
+12:46 < joe7> but, SP and FP are psuedo registers. SP is not the actual stack pointer.
+12:47 < cinap_lenrek> no
+12:47 < joe7> SP is a psuedo register with offsets for this function(?)
+12:47 < sigrid> on 386 and amd64 SP IS actual register
+12:47 < cinap_lenrek> just assemble and link the code you pasted and see
+12:47 < joe7> ok, thanks.
+
+*/
--- /dev/null
+++ b/tests/asmconventions_asm.s
@@ -1,0 +1,14 @@
+
+TEXT asmarg1(SB), 1, $-4
+	MOVQ	SP, 0(RARG)		/* store SP to the address in the first argument */
+	MOVQ	0(SP), BX		/* store return PC - top of stack */
+	MOVQ	BX, 8(RARG)		/*		to the next location at the same address */
+	MOVL	$0, AX			/* return 0 */
+	RET
+
+TEXT asmarg2(SB), 1, $0
+	MOVQ	SP, 0(RARG)		/* store SP to the address in the first argument */
+	MOVQ	0(SP), BX		/* store return PC - top of stack */
+	MOVQ	BX, 8(RARG)		/*		to the next location at the same address */
+	MOVL	$0, AX			/* return 0 */
+	RET