I am studying the ppc32 and ppc64 architecture to implement a new backend for my compiler, but I am in doubt about the prolog of a function, I have read several IBM documents about PowerPC, but I have read little information about the stack. of a program, and how exactly it works.
So to try to understand better, I used godbolt.org to check how the functions are compiled and noticed the following:
A function with 4 parameters of type int, the following code is generate for the prolog:
test1(int, int, int, int):
stwu 1,-32(1)
stw 31,28(1)
mr 31,1
stw 3,8(31)
stw 4,12(31)
stw 5,16(31)
stw 6,20(31)
A function with 5 parameters of type int, the following code is used:
test2(int, int, int, int, int):
stwu 1,-48(1)
stw 31,44(1)
mr 31,1
stw 3,8(31)
stw 4,12(31)
stw 5,16(31)
stw 6,20(31)
stw 7,24(31)
A function with 10 parameters of type int uses the following code:
test4(int, int, int, int, int, int, int, int, int, int):
stwu 1,-48(1)
stw 31,44(1)
mr 31,1
stw 3,8(31)
stw 4,12(31)
stw 5,16(31)
stw 6,20(31)
stw 7,24(31)
stw 8,28(31)
stw 9,32(31)
stw 10,36(31)
I noticed that for the 4- and 5-parameter function, the calculation of the stack offset has changed in the following instructions.
; 4 parameters
stwu 1,-32(1)
stw 31,28(1)
; 5 parameters
stwu 1,-48(1)
stw 31,44(1)
But in the 10-parameter function, the offset remained the same as the 5-parameter one and only 8 'stw' instructions were used to load the parameters, the remaining parameters were loaded later using the 'lwz' instruction
Why does it happen ? I think it's something on the stack, but what?
PS.: The assembly code I showed in this question was generated by GCC 4.8.5 PowerPC
You mention that:
only 8 'stw' instructions were used to load the parameters
- but this isn't code to load the parameters. The parameters are already in registers r3-r10, as that's how they're passed to the function, according to the ELF ABI.
Instead, those stw instructions are storing the function arguments to the stack. It's unclear why the compiler is copying the arguments to the stack without having the function code available, but assuming that your functions are simple, my guess is that you have optimisation disabled, and so it's saving everything to the stack frame.
The reason that there's no difference in the prologue between the 8-argument and 10-argument versions is that only the first 8 arguments are passed via registers. Any further arguments are passed on the caller's stack, and loaded via the lwz instruction.
You should read up on the ELF ABI specification for ppc, which defines the function calling sequence (section 3-14), amongst other things. While you didn't specify which ABI spec you're compiling against, there shouldn't be a whole lot of variation from the ELF spec in this area.
Related
I've been trying to write a pool of database connections based on a lockable queue (well, seq in this case) called POOL. I want to have POOL as a global variable and then use initConnectionPool to instantiate it. I've tried to do so with the code below
var POOL: ConnectionPool
proc initConnectionPool*(initialPoolSize: static int) =
POOL = ConnectionPool(connections: #[])
initLock(POOL.lock)
However, this throws a compiler error:
‘pthread_mutex_t {aka union <anonymous>}’ has no member named ‘abi’
I am not quite sure what this is supposed to mean or what to do about this. How can I fix this issue?
Update: As of nim v. 1.6.6 this appears to no longer be an issue and work flawlessly.
For pre nim v.1.6.6:
This appears to be a known issue. Thankfully xflywind helped me out an pointed me to the right answer.
POOL = ConnectionPool(connections: #[]) within the proc is not allowed and causes the compile issue here. What you should be doing here is, instead of instantiating this object, to just assign the individual fields here as if the object already existed, since, in a way, it already does.
So this will compile:
proc initConnectionPool*(initialPoolSize: static int) =
POOL.connections: #[]
initLock(POOL.lock)
I want all function parameters to be generated on a new line, like so:
fun test(
a: Int,
b: Int,
c: Int
)
How can I do this? At the moment the parameter lists wrap arbitrarily. I have an unknown number of function parameters to generate.
Right now, if the function has more than 2 parameters, each parameter will appear on a new line, otherwise the function signature will be printed on a single line. We don't provide APIs for modifying this behavior.
One of the more notable aspects of Go when coming from C is that the compiler will not build your program if there is an unused variable declared inside of it. So why, then, is this program building if there is an unused parameter declared in a function?
func main() {
print(computron(3, -3));
}
func computron(param_a int, param_b int) int {
return 3 * param_a;
}
There's no official reason, but the reason given on golang-nuts is:
Unused variables are always a programming error, whereas it is common
to write a function that doesn't use all of its arguments.
One could leave those arguments unnamed (using _), but then that might
confuse with functions like
func foo(_ string, _ int) // what's this supposed to do?
The names, even if they're unused, provide important documentation.
Andrew
https://groups.google.com/forum/#!topic/golang-nuts/q09H61oxwWw
Sometimes having unused parameters is important for satisfying interfaces, one example might be a function that operates on a weighted graph. If you want to implement a graph with a uniform cost across all edges, it's useless to consider the nodes:
func (graph *MyGraph) Distance(node1,node2 Node) int {
return 1
}
As that thread notes, there is a valid argument to only allow parameters named as _ if they're unused (e.g. Distance(_,_ Node)), but at this point it's too late due to the Go 1 future-compatibility guarantee. As also mentioned, a possible objection to that anyway is that parameters, even if unused, can implicitly provide documentation.
In short: there's no concrete, specific answer, other than that they simply made an ultimately arbitrary (but still educated) determination that unused parameters are more important and useful than unused local variables and imports. If there was once a strong design reason, it's not documented anywhere.
The main reason is to be able to implement interfaces that dictate specific methods with specific parameters, even if you don't use all of them in your implementation. This is detailed in #Jsor's answer.
Another good reason is that unused (local) variables are often the result of a bug or the use of a language feature (e.g. use of short variable declaration := in a block, unintentionally shadowing an "outer" variable) while unused function parameters never (or very rarely) are the result of a bug.
Another reason can be to provide forward compatibility. If you release a library, you can't change or extend the parameter list without breaking backward compatibility (and in Go there is no function overloading: if you want 2 variants with different parameters, their names must be different too).
You may provide an exported function or method and add extra - not yet used - or optional parameters (e.g. hints) to it in the spirit that you may use them in a future version / release of your library.
Doing so early will give you the benefit that others using your library won't have to change anything in their code.
Let's see an example:
You want to create a formatting function:
// FormatSize formats the specified size (bytes) to a string.
func FormatSize(size int) string {
return fmt.Sprintf("%d bytes", size)
}
You may as well add an extra parameter right away:
// FormatSize formats the specified size (bytes) to a string.
// flags can be used to alter the output format. Not yet used.
func FormatSize(size int, flags int) string {
return fmt.Sprintf("%d bytes", size)
}
Then later you may improve your library and your FormatSize() function to support the following formatting flags:
const (
FlagAutoUnit = 1 << iota // Automatically format as KB, MB, GB etc.
FlagSI // Use SI conversion (1000 instead of 1024)
FlagGroupDecimals // Format number using decimal grouping
)
// FormatSize formats the specified size (bytes) to a string.
// flags can be used to alter the output format.
func FormatSize(size int, flags int) string {
var s string
// Check flags and format accordingly
// ...
return s
}
When we create multiple instances from a data type (class) that has a pass procedure pointer, is the actual procedure (subroutines/functions) copied in each instance? Or is just the pointer copied?
For example consider the following code that compiles and runs correctly.
module mod2
implicit none
private
type class_type
integer :: a, b, c
contains
procedure :: add => add_it
end type class_type
public :: class_type
contains
subroutine add_it(this)
implicit none
class(class_type), intent(inout) :: this
this%c = this%a + this%b
end subroutine add_it
end module mod2
program tester
use mod2
implicit none
type(class_type), dimension(10) :: objs
objs(:) = class_type(1, 2, 0)
end program tester
Is subroutine add_it duplicated in each of the 10 objects created from data type class_type? Or is the instruction-set of subroutine add_it stored somewhere and the pointers to it, i.e. "procedure :: add => add_it" copied in each object?
Typically neither. Note this is very much implementation specific - what I describe below is typical but different processors may do things differently.
Note there are no procedure pointers in your example. The type class_type has a binding. If the class_type had a procedure pointer, things are different.
Typical implementation for bindings is that the compiler creates a table of machine level pointers, with one entry for each specific binding, with the pointer pointing at the code for the procedure. A table (sometimes known as a "vtable", from the similar technique used for virtual member functions in C++ and similar languages) is created for each type in the program.
For polymorphic objects (things declared with CLASS), the compiler then creates a descriptor that has a machine level pointer to the relevant table for the dynamic (runtime) type of the object. This pointer effectively indicates the dynamic type of the object and may be used in constructs such as SELECT TYPE and invocations of things like SAME_TYPE_AS. If you have a polymorphic array the compiler will initially typically only create one descriptor for the entire array, as individual elements in the array must all have the same dynamic type.
When you call a binding on a polymorphic object, the compiler follows the pointer to the vtable, then looks up the relevant pointer to the procedure binding.
No such descriptor or pointer dereferencing is required for non-polymorphic objects (things declared with TYPE) as the dynamic and declared type are always the same, the compiler knows what the declared type is and the compiler knows, at compile time, which procedure will be called.
If you have a procedure call where a non-polymorphic actual argument is associated with a polymorphic dummy argument, then the compiler will typically create the necessary descriptor as part of making the procedure call. Similarly for passing a polymorphic array element to a procedure taking a polymorphic scalar.
The main program of your code contains no polymorphic entities, and you call no procedures, so there may not be any machine pointers back to the vtable.
Procedure pointer components (components declared PROCEDURE(xxx), POINTER :: yyy before the CONTAINS of the type declaration) can be different for every object (including being different for every element in an array). In that case typical implementation is to store a machine level pointer to the code for the relevant procedure (or a null pointer if the procedure pointer component has not been associated).
This is a question for the older programmers.
Years ago, I encountered a dialect of Pascal which allowed a variable number of arguments, through some kind of extension.
Does anyone know of a current dialect of Pascal which allows a variable number of arguments?
Given that Pascal is not as popular as it used to be, I wouldn't be surprised if the answer is no.
BTW, it is more correct, isn't it, to say variable number of arguments, rather than parameters ?
No. The answer is based on the Pascal dialects that I have used; others may be different.
The reason is that Pascal pushes arguments onto the stack frame in order, so all arguments are accessed via a fixed offset from the stack pointer. C, by comparison, pushes arguments in reverse order, so defined parameters are at fixed offset, and you can access "extra" arguments via pointer arithmetic. I'll try some ASCII art:
Pascal C
---------------------
| extra arg |
--------------------- ---------------------
| 1st param | | 3rd param |
--------------------- ---------------------
| 2nd param | | 2nd param |
--------------------- ---------------------
SP -> | 3rd param | | 1st param |
--------------------- ---------------------
As for parameter versus argument: as I learned it, the function (method) defines its parameters, the caller passes arguments. That definition came, I believe, from a Fortran manual, so that should give you an idea of how old I am :-)
You can use optional arguments with delphi to get the same effect:
procedure Proc(const A: Integer; const B: Integer = 15);
Proc(10); // B = 15
Proc(20,30);
Or overloaded methods:
procedure Proc(const A: Integer); overload;
procedure Proc(const A,B: Integer); overload;
Proc(10); // Variant 1
Proc(20,30); // Variant 2
Or you can use a variable array for parameters:
procedure Message(const AMessage: string; const AArgs: array of const);
Message('Hello %s', [Name]);
Message('%s %s', [Greeting, Name]);
GNU-Pascal (gcc based) afaik maps 1:1 to C support. using function something(arg:pchar;...) like syntax
Delphi/Free Pascal has "array of const" support, which is a typesafe version, and a varargs directive for the C interfacing (D6 or D7+)
You are probably thinking of a library that was available for Turbo Pascal where they had a hack like this. My syntax is a bit a rusty for objects and descending from it.
type
TValue = object;
TInteger = object(TValue)
Value : Integer;
end
TString = object(TValue)
Value : String;
end
TParam = record
Value : TValue;
Param : TParam;
end;
TValue = object;
{ Definition of Function }
function Test (Arg : TParam);
{ Usage }
var
I : TInteger;
S : TString;
Test (TParam (I, TParam (S, nil));
You could just chain as many arguments as you wanted. The last one had to be terminated with nil.
Yes!
Use the params keyword:
procedure write(params args: array of Object);
begin
{...}
end;