Chez Scheme: generalize __varargs to (__varargs_after <n>)

The non-standard ARM Mac OS ABI doesn't just use a different
convention if the function has varargs: it puts each vararg in a
different place than a non-vararg argument of the same type and
position. So, `foreign-procedure` and `foreign-callable` need to know
where varargs start. A `__varargs` declaration is shorthand for
`(__varargs_after 1)`.

For PPC32 Mac OS, we retain the trick that makes varargs foreign calls
work without a `__varargs` declaration, but `(__varargs_after <n>)`
fixes up callable support --- in the extremely unlikely case that
someone needs general varargs callables on PPC32 Mac OS.
This commit is contained in:
Matthew Flatt 2020-12-20 11:21:20 -07:00
parent 0ce89f53c4
commit 6033237ed6
9 changed files with 436 additions and 259 deletions

View File

@ -221,9 +221,11 @@ Since \scheme{__cdecl} is the default, specifying \scheme{__cdecl} is
equivalent to specifying \scheme{#f} or no convention.
Finally, \var{conv} can be \scheme{__collect_safe} to indicate that garbage
collection is allowed concurrent to a call of the foreign procedure, or it
can be \var{conv} can be \scheme{__varargs} to indicate that the procedure
uses a convention that works with a variable number of arguments (which
differs from the fixed-argument variant for some platforms and conventions).
can be \var{conv} can be \scheme{__varargs} or \scheme{(__varargs_after \var{n})} to indicate
that the procedure uses a convention that works with a variable number of arguments
after the first \var{n} (which
differs from the fixed-argument variant for some platforms and conventions),
where \scheme{__varargs} is a shorthand for \scheme{(__varargs_after 1)}.
Use \scheme{__stdcall} to access most Windows API procedures.
Use \scheme{__cdecl} for Windows API varargs procedures,

View File

@ -1044,10 +1044,13 @@
(define call-df (foreign-procedure "call_df" (ptr double-float int int) double-float))
(define call-varargs-df (foreign-procedure "call_varargs_df" (ptr double-float int int) double-float))
(define call-varargs-i7df (foreign-procedure "call_varargs_i7df" (ptr int
double-float double-float double-float
double-float double-float double-float
double-float)
double-float double-float double-float
double-float double-float double-float
double-float)
double-float))
(define call-varargs-dfii (foreign-procedure "call_varargs_dfii" (ptr double-float int int) double-float))
(define call-varargs-dfidf (foreign-procedure "call_varargs_dfidf" (ptr double-float int double-float) double-float))
(define call-varargs-dfsfi (foreign-procedure "call_varargs_dfsfi" (ptr double-float single-float int) double-float))
(define ($test-call-int signed? size call-int make-fc)
(define n10000 (expt 256 size))
(define nffff (- n10000 1))
@ -1178,6 +1181,35 @@
1 2.2 3.2 4.5 6.7 8.9 10.1 11.5)
55.1)
(equal?
(call-varargs-dfii
(foreign-callable
(__varargs_after 2)
(lambda (x y z) (+ x y z))
(double-float int int) double-float)
10.25 20 300)
620.25)
(equal?
(call-varargs-dfidf
(foreign-callable
(__varargs_after 2)
(lambda (x y z) (+ x y z))
(double-float int double-float) double-float)
10.25 20 300.25)
330.75)
(equal?
(call-varargs-dfsfi
(foreign-callable
(__varargs_after 2)
(lambda (x y z) (+ x y z))
(double-float single-float int) double-float)
10.25 20.0 300)
620.5)
;(define call-varargs-dfsfi (foreign-procedure #;__varargs #;2 "call_varargs_dfsfi" (ptr double-float single-float int) double-float))
(error?
(call-i8
(foreign-callable
@ -2738,6 +2770,12 @@
[(i3osx ti3osx a6osx ta6osx ppc32osx tppc32osx arm64osx tarm64osx)
'(load-shared-object "libc.dylib")]
[else (error 'load-libc "unrecognized machine type ~s" (machine-type))]))
(define varargs_df (foreign-procedure (__varargs_after 1) "varargs_df" (double int int) double))
(define varargs_dfii (foreign-procedure (__varargs_after 2) "varargs_dfii" (double int int) double))
(define varargs_dfidf (foreign-procedure (__varargs_after 2) "varargs_dfidf" (double int double) double))
(define varargs_sfdfi (foreign-procedure (__varargs_after 2) "varargs_sfdfi" (float double int) double))
(define varargs_i7df (foreign-procedure (__varargs_after 1) "varargs_i7df" (int double double double double double double double)
double))
#t)
(equal?
(with-input-from-string
@ -2798,6 +2836,22 @@
(double-float double-float) single-float)
3.5 -5.25)))
3.25)
(equal?
(varargs_df 13.5 7 10)
30.5)
(equal?
(varargs_dfii 13.5 -7 -10)
-3.5)
(equal?
(varargs_dfidf 13.5 10 7.5)
31.0)
(equal?
(varargs_sfdfi 10.5 3.25 8)
21.75)
(equal?
(varargs_i7df 1 2.0 3.0 4.0 5.0 6.0 7.0 8.0)
36.0)
)
(mat structs

View File

@ -16,6 +16,7 @@
#include <stdio.h>
#include <wchar.h>
#include <stdarg.h>
#ifdef _WIN32
# define SCHEME_IMPORT
@ -244,10 +245,27 @@ EXPORT double_float call_df(ptr code, double_float x, int m, int k) {
return (*((double_float (*) (double_float))Sforeign_callable_entry_point(code)))(x + m) + k;
}
/* varargs after 1 argument */
EXPORT double_float call_varargs_df(ptr code, double_float x, int m, int k) {
return (*((double_float (*) (double, ...))Sforeign_callable_entry_point(code)))(x - m, x + m) + k;
}
/* varargs after 2 arguments */
EXPORT double_float call_varargs_dfii(ptr code, double_float x, int m, int k) {
return (*((double_float (*) (double, int, ...))Sforeign_callable_entry_point(code)))(x - m, x + m, k) + k;
}
/* varargs after 2 arguments */
EXPORT double_float call_varargs_dfidf(ptr code, double_float x, int m, double k) {
return (*((double_float (*) (double, int, ...))Sforeign_callable_entry_point(code)))(x - m, x + m, x) + k;
}
/* varargs after 2 arguments */
EXPORT double_float call_varargs_dfsfi(ptr code, double_float x, single_float m, int k) {
return (*((double_float (*) (double, float, ...))Sforeign_callable_entry_point(code)))(x - m, x + m, k) + k;
}
/* varargs after 1 argument */
EXPORT double_float call_varargs_i7df(ptr code, int i,
double_float a, double_float b, double_float c,
double_float d, double_float e, double_float f,
@ -255,6 +273,62 @@ EXPORT double_float call_varargs_i7df(ptr code, int i,
return (*((double_float (*) (int, ...))Sforeign_callable_entry_point(code)))(i, a, b, c, d, e, f, g);
}
EXPORT double_float varargs_df(double_float x, ...) {
va_list va;
int m, k;
va_start(va, x);
m = va_arg(va, int);
k = va_arg(va, int);
va_end(va);
return x + m + k;
}
EXPORT double_float varargs_dfii(double_float x, int m, ...) {
va_list va;
int k;
va_start(va, m);
k = va_arg(va, int);
va_end(va);
return x + m + k;
}
EXPORT double_float varargs_dfidf(double_float x, int m, ...) {
va_list va;
double k;
va_start(va, m);
k = va_arg(va, double);
va_end(va);
return x + m + k;
}
EXPORT double_float varargs_sfdfi(single_float x, double_float m, ...) {
va_list va;
int k;
va_start(va, m);
k = va_arg(va, int);
va_end(va);
return x + m + k;
}
EXPORT double_float varargs_i7df(int i, ...) {
va_list va;
double_float a, b, c;
double_float d, e, f;
double_float g;
va_start(va, i);
a = va_arg(va, double_float);
b = va_arg(va, double_float);
c = va_arg(va, double_float);
d = va_arg(va, double_float);
e = va_arg(va, double_float);
f = va_arg(va, double_float);
g = va_arg(va, double_float);
va_end(va);
return a + b + c + d + e + f + g + i;
}
EXPORT u8 *u8_star_to_u8_star(u8 *s) {
return s == (u8 *)0 ? (u8 *)0 : s + 1;
}

View File

@ -2770,7 +2770,7 @@
(safe-assert (reg-callee-save? %tc)) ; no need to save-restore
(let* ([arg-type* (info-foreign-arg-type* info)]
[conv* (info-foreign-conv* info)]
[varargs? (memq 'varargs conv*)]
[varargs? (ormap (lambda (conv) (and (pair? conv) (eq? (car conv) 'varargs))) conv*)]
[result-type (info-foreign-result-type info)]
[result-reg* (get-result-regs result-type varargs?)]
[fill-result-here? (indirect-result-that-fits-in-registers? result-type)]
@ -3240,7 +3240,7 @@
(vector->list regvec)))
(let* ([arg-type* (info-foreign-arg-type* info)]
[conv* (info-foreign-conv* info)]
[varargs? (memq 'varargs conv*)]
[varargs? (ormap (lambda (conv) (and (pair? conv) (eq? (car conv) 'varargs))) conv*)]
[result-type (info-foreign-result-type info)]
[synthesize-first? (indirect-result-that-fits-in-registers? result-type)]
[adjust-active? (if-feature pthreads (memq 'adjust-active conv*) #f)])

View File

@ -2421,6 +2421,11 @@
(inline ,(make-info-kill*-live* fp-regs '()) ,%pop-fpmultiple))])))])
(save-and-restore-gp regs (save-and-restore-fp regs e))))))
(define (extract-varargs-after-conv conv*)
(ormap (lambda (conv)
(and (pair? conv) (eq? (car conv) 'varargs) (cdr conv)))
conv*))
(define-record-type cat
(nongenerative #{cat jqrttgvpydsbdo0l736l43udu-1})
(sealed #t)
@ -2431,11 +2436,11 @@
indirect-bytes)) ; #f or extra bytes on stack for indirect
(define alignment-via-lookahead
(lambda (size types stack-align varargs? k)
(lambda (size types stack-align varargs-after k)
(constant-case machine-type-name
[(arm64osx tarm64osx)
(cond
[varargs? (k (align 8 size) 0 0)]
[(eqv? 0 varargs-after) (k (align 8 size) 0 0)]
[else
;; On Mac OS, a non-varargs stack argument does not have to use a
;; multiple of 8, but we need to work out any padding that
@ -2463,14 +2468,12 @@
(k (align 8 size) 0 0)])))
(define rest-of
(lambda (regs n varargs?)
(lambda (regs n next-varargs-after)
(constant-case machine-type-name
[(arm64osx tarm64osx)
(cond
[varargs?
;; Assume (arbitraily) that all but the first argument
;; is varargs, which means that all the rest go on the
;; stack.
[(eqv? next-varargs-after 0)
;; All the rest go on the stack
'()]
[else
(list-tail regs n)])]
@ -2478,108 +2481,123 @@
(list-tail regs n)])))
(define categorize-arguments
(lambda (types varargs?)
(lambda (types varargs-after)
(let loop ([types types] [int* (int-argument-regs)] [fp* (fp-argument-regs)]
[varargs-after varargs-after]
;; accumulate alignment from previous args so we can compute any
;; needed padding and alignment after this next argument
[stack-align 0])
(if (null? types)
'()
(nanopass-case (Ltype Type) (car types)
[(fp-double-float)
(cond
[(null? fp*)
(cons (make-cat 'stack '() 8 0 #f) (loop (cdr types) int* '() 0))]
[else
(cons (make-cat 'fp (list (car fp*)) 8 0 #f)
(loop (cdr types) (rest-of int* 0 varargs?) (rest-of fp* 1 varargs?) stack-align))])]
[(fp-single-float)
(cond
[(null? fp*)
(alignment-via-lookahead
4 (cdr types) stack-align varargs?
(lambda (bytes pad stack-align)
(cons (make-cat 'stack '() bytes pad #f) (loop (cdr types) int* '() stack-align))))]
[else
(cons (make-cat 'fp (list (car fp*)) 8 0 #f)
(loop (cdr types) (rest-of int* 0 varargs?)(rest-of fp* 1 varargs?) stack-align))])]
[(fp-ftd& ,ftd)
(let* ([size ($ftd-size ftd)]
[members ($ftd->members ftd)]
[num-members (length members)]
[doubles? (and (fx= 8 ($ftd-alignment ftd))
(fx<= num-members 4)
(andmap double-member? members))]
[floats? (and (fx= 4 ($ftd-alignment ftd))
(fx<= num-members 4)
(andmap float-member? members))])
(let ([next-varargs-after (and varargs-after (if (fx> varargs-after 0) (fx- varargs-after 1) 0))])
(if (null? types)
'()
(nanopass-case (Ltype Type) (car types)
[(fp-double-float)
(cond
[doubles?
;; Sequence of up to 4 doubles that may fit in registers
(cond
[(fx>= (length fp*) num-members)
;; Allocate each double to a register
(cons (make-cat 'fp (list-head fp* num-members) (fx* 8 num-members) 0 #f)
(loop (cdr types) (rest-of int* 0 varargs?) (rest-of fp* num-members varargs?) stack-align))]
[else
;; Stop using fp registers, put on stack
(cons (make-cat 'stack '() size 0 #f)
(loop (cdr types) int* '() 0))])]
[floats?
;; Sequence of up to 4 floats that may fit in registers
(cond
[(fx>= (length fp*) num-members)
;; Allocate each float to a register
(cons (make-cat 'fp (list-head fp* num-members) (fx* 8 num-members) 0 #f)
(loop (cdr types) (rest-of int* 0 varargs?) (rest-of fp* num-members varargs?) stack-align))]
[else
;; Stop using fp registers, put on stack
(alignment-via-lookahead
size (cdr types) stack-align varargs?
(lambda (size pad stack-align)
(cons (make-cat 'stack '() size pad #f)
(loop (cdr types) int* '() stack-align))))])]
[(fx> size 16)
;; Indirect; pointer goes in a register or on the stack
(cond
[(null? int*)
;; Pointer on the stack
(cons (make-cat 'stack '() (constant ptr-bytes) 0 (align 8 size))
(loop (cdr types) '() fp* 0))]
[else
;; Pointer in register
(cons (make-cat 'int (list (car int*)) 8 0 (align 8 size))
(loop (cdr types) (rest-of int* 1 varargs?) (rest-of fp* 0 varargs?) stack-align))])]
[(null? fp*)
(cons (make-cat 'stack '() 8 0 #f) (loop (cdr types) int* '() next-varargs-after 0))]
[else
;; Maybe put in integer registers
(let* ([regs (fxquotient (align 8 size) 8)])
(cons (make-cat 'fp (list (car fp*)) 8 0 #f)
(loop (cdr types) (rest-of int* 0 next-varargs-after) (rest-of fp* 1 next-varargs-after)
next-varargs-after
stack-align))])]
[(fp-single-float)
(cond
[(null? fp*)
(alignment-via-lookahead
4 (cdr types) stack-align varargs-after
(lambda (bytes pad stack-align)
(cons (make-cat 'stack '() bytes pad #f) (loop (cdr types) int* '() next-varargs-after stack-align))))]
[else
(cons (make-cat 'fp (list (car fp*)) 8 0 #f)
(loop (cdr types) (rest-of int* 0 next-varargs-after)(rest-of fp* 1 next-varargs-after)
next-varargs-after
stack-align))])]
[(fp-ftd& ,ftd)
(let* ([size ($ftd-size ftd)]
[members ($ftd->members ftd)]
[num-members (length members)]
[doubles? (and (fx= 8 ($ftd-alignment ftd))
(fx<= num-members 4)
(andmap double-member? members))]
[floats? (and (fx= 4 ($ftd-alignment ftd))
(fx<= num-members 4)
(andmap float-member? members))])
(cond
[doubles?
;; Sequence of up to 4 doubles that may fit in registers
(cond
[(fx<= regs (length int*))
;; Fits in registers
(cons (make-cat 'int (list-head int* regs) (align 8 size) 0 #f)
(loop (cdr types) (rest-of int* regs varargs?) (rest-of fp* 0 varargs?) stack-align))]
[(fx>= (length fp*) num-members)
;; Allocate each double to a register
(cons (make-cat 'fp (list-head fp* num-members) (fx* 8 num-members) 0 #f)
(loop (cdr types) (rest-of int* 0 next-varargs-after) (rest-of fp* num-members next-varargs-after)
next-varargs-after
stack-align))]
[else
;; Stop using int registers, put on stack
;; Stop using fp registers, put on stack
(cons (make-cat 'stack '() size 0 #f)
(loop (cdr types) int* '() next-varargs-after 0))])]
[floats?
;; Sequence of up to 4 floats that may fit in registers
(cond
[(fx>= (length fp*) num-members)
;; Allocate each float to a register
(cons (make-cat 'fp (list-head fp* num-members) (fx* 8 num-members) 0 #f)
(loop (cdr types) (rest-of int* 0 next-varargs-after) (rest-of fp* num-members next-varargs-after)
next-varargs-after
stack-align))]
[else
;; Stop using fp registers, put on stack
(alignment-via-lookahead
size (cdr types) stack-align varargs?
size (cdr types) stack-align varargs-after
(lambda (size pad stack-align)
(cons (make-cat 'stack '() size pad #f)
(loop (cdr types) '() fp* stack-align))))]))]))]
[else
;; integers, scheme-object, etc.
(cond
[(null? int*)
(let ([size (nanopass-case (Ltype Type) (car types)
[(fp-integer ,bits) (fxquotient bits 8)]
[(fp-unsigned ,bits) (fxquotient bits 8)]
[else 8])])
(alignment-via-lookahead
size (cdr types) stack-align varargs?
(lambda (size pad stack-align)
(cons (make-cat 'stack '() size pad #f) (loop (cdr types) '() fp* stack-align)))))]
[else
(cons (make-cat 'int (list (car int*)) 8 0 #f)
(loop (cdr types) (rest-of int* 1 varargs?) (rest-of fp* 0 varargs?) stack-align))])])))))
(loop (cdr types) int* '() next-varargs-after stack-align))))])]
[(fx> size 16)
;; Indirect; pointer goes in a register or on the stack
(cond
[(null? int*)
;; Pointer on the stack
(cons (make-cat 'stack '() (constant ptr-bytes) 0 (align 8 size))
(loop (cdr types) '() fp* next-varargs-after 0))]
[else
;; Pointer in register
(cons (make-cat 'int (list (car int*)) 8 0 (align 8 size))
(loop (cdr types) (rest-of int* 1 next-varargs-after) (rest-of fp* 0 next-varargs-after)
next-varargs-after
stack-align))])]
[else
;; Maybe put in integer registers
(let* ([regs (fxquotient (align 8 size) 8)])
(cond
[(fx<= regs (length int*))
;; Fits in registers
(cons (make-cat 'int (list-head int* regs) (align 8 size) 0 #f)
(loop (cdr types) (rest-of int* regs next-varargs-after) (rest-of fp* 0 next-varargs-after)
next-varargs-after
stack-align))]
[else
;; Stop using int registers, put on stack
(alignment-via-lookahead
size (cdr types) stack-align varargs-after
(lambda (size pad stack-align)
(cons (make-cat 'stack '() size pad #f)
(loop (cdr types) '() fp* next-varargs-after stack-align))))]))]))]
[else
;; integers, scheme-object, etc.
(cond
[(null? int*)
(let ([size (nanopass-case (Ltype Type) (car types)
[(fp-integer ,bits) (fxquotient bits 8)]
[(fp-unsigned ,bits) (fxquotient bits 8)]
[else 8])])
(alignment-via-lookahead
size (cdr types) stack-align varargs-after
(lambda (size pad stack-align)
(cons (make-cat 'stack '() size pad #f) (loop (cdr types) '() fp* next-varargs-after stack-align)))))]
[else
(cons (make-cat 'int (list (car int*)) 8 0 #f)
(loop (cdr types) (rest-of int* 1 next-varargs-after) (rest-of fp* 0 next-varargs-after)
next-varargs-after stack-align))])]))))))
(define get-registers
(lambda (cats kind)
@ -2892,7 +2910,7 @@
(cdr arg-type*)
arg-type*)]
[conv* (info-foreign-conv* info)]
[arg-cat* (categorize-arguments arg-type* (memq 'varargs conv*))]
[arg-cat* (categorize-arguments arg-type* (extract-varargs-after-conv conv*))]
[result-cat (car (categorize-arguments (list result-type) #f))]
[result-reg* (cat-regs result-cat)]
[fill-result-here? (and ftd-result?
@ -3225,7 +3243,7 @@
(cdr arg-type*)
arg-type*)]
[conv* (info-foreign-conv* info)]
[arg-cat* (categorize-arguments arg-type* (memq 'varargs conv*))]
[arg-cat* (categorize-arguments arg-type* (extract-varargs-after-conv conv*))]
[result-cat (car (categorize-arguments (list result-type) #f))]
[synthesize-first? (and ftd-result?
(not (cat-indirect-bytes result-cat))

View File

@ -157,7 +157,9 @@
(define convention?
(lambda (x)
(symbol? x)))
(or (symbol? x)
(and (pair? x)
(eq? 'varargs (car x))))))
(define-record-type preinfo
(nongenerative #{preinfo e23pkvo5btgapnzomqgegm-2})

View File

@ -556,7 +556,7 @@ ftype operators:
(make-ftd-function rtd/fptr
(and defid (symbol->string (syntax->datum defid)))
stype #f #f
($filter-conv 'function-ftype #'(conv ...))
($filter-conv 'function-ftype #'(conv ...) (length #'(arg-type ...)))
(map (lambda (x) (filter-type r x #f)) #'(arg-type ...))
(filter-type r #'result-type #t)))]
[(packed-kwd ftype)

View File

@ -3057,13 +3057,9 @@
;; we push all of the int reg args with one push instruction and all of the
;; float reg args with another (v)push instruction. It's possible for an argument
;; to be split across a register and the stack --- but in that case, there's
;; room just before on the stack to copy in the register. For varrags, the
;; `__varargs` isn't technically enough information, because the ABI is specified
;; in terms of specific unknown arguments (i.e., the part in the "..."); we
;; just assume that all argument except the first are in "...", and that assumption
;; only matters for `double` arguments.
;; room just before on the stack to copy in the register.
(lambda (types gp-reg-count fp-reg-count init-int-reg-offset float-reg-offset stack-arg-offset
synthesize-first-argument? varargs? return-space-offset)
synthesize-first-argument? varargs-after return-space-offset)
(let loop ([types (if synthesize-first-argument? (cdr types) types)]
[locs '()]
[iint 0]
@ -3071,133 +3067,144 @@
[int-reg-offset init-int-reg-offset]
[float-reg-offset float-reg-offset]
[stack-arg-offset (fx- stack-arg-offset (fx- stack-arguments-starting-offset
register+stack-arguments-starting-offset))])
(if (null? types)
(let ([locs (reverse locs)])
(if synthesize-first-argument?
(cons (load-stack-address return-space-offset)
locs)
locs))
(cond
[(nanopass-case (Ltype Type) (car types)
[(fp-double-float) 2]
[(fp-single-float) 1]
[else #f])
=> (lambda (width)
(let ([size (fx* width 4)])
(cond
[(and (fx< iflt fp-reg-count)
(or (not varargs?)
;; hack: varargs function requires at least one argument
(fx= int-reg-offset init-int-reg-offset)))
;; in FP register
register+stack-arguments-starting-offset))]
[varargs-after varargs-after])
(let ([next-varargs-after (and varargs-after (if (fx> varargs-after 0) (fx- varargs-after 1) 0))])
(if (null? types)
(let ([locs (reverse locs)])
(if synthesize-first-argument?
(cons (load-stack-address return-space-offset)
locs)
locs))
(cond
[(nanopass-case (Ltype Type) (car types)
[(fp-double-float) 2]
[(fp-single-float) 1]
[else #f])
=> (lambda (width)
(let ([size (fx* width 4)])
(cond
[(and (fx< iflt fp-reg-count)
(not (eq? varargs-after 0)))
;; in FP register
(loop (cdr types)
(cons (load-double-stack float-reg-offset) locs)
(fx+ iint width) (fx+ iflt 1) (fx+ int-reg-offset size) (fx+ float-reg-offset size)
(fx+ stack-arg-offset size)
next-varargs-after)]
[(or (not (eq? varargs-after 0))
(fx>= iint gp-reg-count))
;; on stack
(loop (cdr types)
(cons (load-double-stack stack-arg-offset) locs)
iint iflt int-reg-offset float-reg-offset
(fx+ stack-arg-offset size)
next-varargs-after)]
[else ;; => varargs
;; in integer register --- but maybe halfway on stack
(loop (cdr types)
(cons (if (fx< (fx+ iint 1) gp-reg-count)
(load-double-stack int-reg-offset)
(load-split-double-stack int-reg-offset (fx+ stack-arg-offset 4)))
locs)
(fx+ iint width) iflt (fx+ int-reg-offset size) float-reg-offset
(fx+ stack-arg-offset size)
next-varargs-after)])))]
[(nanopass-case (Ltype Type) (car types)
[(fp-ftd& ,ftd) ftd]
[else #f])
=>
(lambda (ftd)
(let ([members ($ftd->members ftd)])
(cond
[(and (not ($ftd-union? ftd))
(pair? members)
(null? (cdr members))
(eq? 'float (caar members))
(fx< iflt fp-reg-count))
;; single member as float => in register
(let ([load-address (case ($ftd-size ftd)
[(4) load-stack-address/convert-float]
[else load-stack-address])]
[size ($ftd-size ftd)])
(loop (cdr types)
(cons (load-address float-reg-offset) locs)
(fx+ iint (fxsrl size 2)) (fx+ iflt 1) (fx+ int-reg-offset size) (fx+ float-reg-offset 8)
(fx+ stack-arg-offset size)
next-varargs-after))]
[(memv ($ftd-size ftd) '(1 2))
;; byte or word; need to load address into middle
(loop (cdr types)
(cons (load-double-stack float-reg-offset) locs)
(fx+ iint width) (fx+ iflt 1) (fx+ int-reg-offset size) (fx+ float-reg-offset size)
(fx+ stack-arg-offset size))]
[(or (not varargs?)
(fx>= iint gp-reg-count))
;; on stack
(loop (cdr types)
(cons (load-double-stack stack-arg-offset) locs)
iint iflt int-reg-offset float-reg-offset
(fx+ stack-arg-offset size))]
[else ;; => varargs
;; in integer register --- but maybe halfway on stack
(loop (cdr types)
(cons (if (fx< (fx+ iint 1) gp-reg-count)
(load-double-stack int-reg-offset)
(load-split-double-stack int-reg-offset (fx+ stack-arg-offset 4)))
(cons (load-stack-address (fx+ (fx- 4 ($ftd-size ftd))
(if (< iint gp-reg-count)
int-reg-offset
stack-arg-offset)))
locs)
(fx+ iint width) iflt (fx+ int-reg-offset size) float-reg-offset
(fx+ stack-arg-offset size))])))]
[(nanopass-case (Ltype Type) (car types)
[(fp-ftd& ,ftd) ftd]
[else #f])
=>
(lambda (ftd)
(let ([members ($ftd->members ftd)])
(cond
[(and (not ($ftd-union? ftd))
(pair? members)
(null? (cdr members))
(eq? 'float (caar members))
(fx< iflt fp-reg-count))
;; single member as float => in register
(let ([load-address (case ($ftd-size ftd)
[(4) load-stack-address/convert-float]
[else load-stack-address])]
[size ($ftd-size ftd)])
(loop (cdr types)
(cons (load-address float-reg-offset) locs)
(fx+ iint (fxsrl size 2)) (fx+ iflt 1) (fx+ int-reg-offset size) (fx+ float-reg-offset 8)
(fx+ stack-arg-offset size)))]
[(memv ($ftd-size ftd) '(1 2))
;; byte or word; need to load address into middle
(loop (cdr types)
(cons (load-stack-address (fx+ (fx- 4 ($ftd-size ftd))
(if (< iint gp-reg-count)
int-reg-offset
stack-arg-offset)))
locs)
(fx+ iint 1) iflt (fx+ int-reg-offset 4) float-reg-offset
(fx+ stack-arg-offset 4))]
[else
;; in registers until they run out; copy the registers
;; to the reserved space just before arguments that
;; are only on the stack, and then we have a contiguous
;; object on the stack; except that sizes not a multiple
;; of 4 are always on the stack and no copying is needed
(let* ([size ($ftd-size ftd)]
[words (fxsrl (align 4 size) 2)]
[loc
(cond
[(not (fx= size (fx* words 4)))
(load-stack-address stack-arg-offset)]
[else
(let c-loop ([size size] [iint iint] [offset 0])
(cond
[(or (fx<= size 0)
(fx>= iint gp-reg-count))
(fx+ iint 1) iflt (fx+ int-reg-offset 4) float-reg-offset
(fx+ stack-arg-offset 4)
next-varargs-after)]
[else
;; in registers until they run out; copy the registers
;; to the reserved space just before arguments that
;; are only on the stack, and then we have a contiguous
;; object on the stack; except that sizes not a multiple
;; of 4 are always on the stack and no copying is needed
(let* ([size ($ftd-size ftd)]
[words (fxsrl (align 4 size) 2)]
[loc
(cond
[(not (fx= size (fx* words 4)))
(load-stack-address stack-arg-offset)]
[else
(let ([loc (c-loop (fx- size 4) (fx+ iint 1) (fx+ offset 4))]
[tmp %Carg8])
(lambda (lvalue)
(%seq
(set! ,tmp ,(%mref ,%sp ,(fx+ int-reg-offset offset)))
(set! ,(%mref ,%sp ,(fx+ stack-arg-offset offset)) ,tmp)
,(loc lvalue))))]))])])
(loop (cdr types)
(cons loc locs)
(fx+ iint words) iflt (fx+ int-reg-offset (fx* 4 words)) float-reg-offset
(fx+ stack-arg-offset (fx* 4 words))))])))]
[(nanopass-case (Ltype Type) (car types)
[(fp-integer ,bits) (fx= bits 64)]
[(fp-unsigned ,bits) (fx= bits 64)]
[else #f])
(cond
[(fx< (fx+ iint 1) gp-reg-count)
(loop (cdr types)
(cons (load-int64-stack int-reg-offset) locs)
(fx+ iint 2) iflt (fx+ int-reg-offset 8) float-reg-offset (fx+ stack-arg-offset 8))]
[(fx< iint gp-reg-count)
;; split across a register and the stack
(loop (cdr types)
(cons (load-split-int64-stack int-reg-offset stack-arg-offset) locs)
(fx+ iint 1) iflt (fx+ int-reg-offset 4) float-reg-offset (fx+ stack-arg-offset 8))]
[else
(loop (cdr types)
(cons (load-int64-stack stack-arg-offset) locs)
iint iflt int-reg-offset float-reg-offset (fx+ stack-arg-offset 8))])]
[else
(if (fx< iint gp-reg-count)
(loop (cdr types)
(cons (load-int-stack (car types) int-reg-offset) locs)
(fx+ iint 1) iflt (fx+ int-reg-offset 4) float-reg-offset (fx+ stack-arg-offset 4))
(loop (cdr types)
(cons (load-int-stack (car types) stack-arg-offset) locs)
iint iflt int-reg-offset float-reg-offset (fx+ stack-arg-offset 4)))])))))
(let c-loop ([size size] [iint iint] [offset 0])
(cond
[(or (fx<= size 0)
(fx>= iint gp-reg-count))
(load-stack-address stack-arg-offset)]
[else
(let ([loc (c-loop (fx- size 4) (fx+ iint 1) (fx+ offset 4))]
[tmp %Carg8])
(lambda (lvalue)
(%seq
(set! ,tmp ,(%mref ,%sp ,(fx+ int-reg-offset offset)))
(set! ,(%mref ,%sp ,(fx+ stack-arg-offset offset)) ,tmp)
,(loc lvalue))))]))])])
(loop (cdr types)
(cons loc locs)
(fx+ iint words) iflt (fx+ int-reg-offset (fx* 4 words)) float-reg-offset
(fx+ stack-arg-offset (fx* 4 words))
next-varargs-after))])))]
[(nanopass-case (Ltype Type) (car types)
[(fp-integer ,bits) (fx= bits 64)]
[(fp-unsigned ,bits) (fx= bits 64)]
[else #f])
(cond
[(fx< (fx+ iint 1) gp-reg-count)
(loop (cdr types)
(cons (load-int64-stack int-reg-offset) locs)
(fx+ iint 2) iflt (fx+ int-reg-offset 8) float-reg-offset (fx+ stack-arg-offset 8)
next-varargs-after)]
[(fx< iint gp-reg-count)
;; split across a register and the stack
(loop (cdr types)
(cons (load-split-int64-stack int-reg-offset stack-arg-offset) locs)
(fx+ iint 1) iflt (fx+ int-reg-offset 4) float-reg-offset (fx+ stack-arg-offset 8)
next-varargs-after)]
[else
(loop (cdr types)
(cons (load-int64-stack stack-arg-offset) locs)
iint iflt int-reg-offset float-reg-offset (fx+ stack-arg-offset 8)
next-varargs-after)])]
[else
(if (fx< iint gp-reg-count)
(loop (cdr types)
(cons (load-int-stack (car types) int-reg-offset) locs)
(fx+ iint 1) iflt (fx+ int-reg-offset 4) float-reg-offset (fx+ stack-arg-offset 4)
next-varargs-after)
(loop (cdr types)
(cons (load-int-stack (car types) stack-arg-offset) locs)
iint iflt int-reg-offset float-reg-offset (fx+ stack-arg-offset 4)
next-varargs-after))]))))))
(define count-reg-args
(lambda (types gp-reg-count fp-reg-count synthesize-first-argument?)
(let f ([types types] [iint (if synthesize-first-argument? -1 0)] [iflt 0])
@ -3248,7 +3255,7 @@
;; we push all of the int reg args with one push instruction and all of the
;; float reg args with another (v)push instruction
(lambda (types gp-reg-count fp-reg-count int-reg-offset float-reg-offset stack-arg-offset
synthesize-first-argument? varargs? return-space-offset)
synthesize-first-argument? varargs-after return-space-offset)
(let loop ([types (if synthesize-first-argument? (cdr types) types)]
[locs '()]
[iint 0]
@ -3524,7 +3531,8 @@
[callee-save-fp-offset (fx+ (fx* isaved 4) callee-save-offset)]
[synthesize-first-argument? (indirect-result-that-fits-in-registers? result-type)]
[adjust-active? (if-feature pthreads (memq 'adjust-active (info-foreign-conv* info)) #f)]
[varargs? (memq 'varargs (info-foreign-conv* info))]
[varargs-after (ormap (lambda (conv) (and (pair? conv) (eq? 'varargs (car conv)) (cdr conv)))
(info-foreign-conv* info))]
[unactivate-mode-offset (fx+ (fx* fpsaved 8) callee-save-fp-offset)]
[return-space-offset (align 8 (fx+ unactivate-mode-offset (if adjust-active? 4 0)))]
[stack-size (align 16 (fx+ return-space-offset (if synthesize-first-argument? 8 0)))]
@ -3555,7 +3563,7 @@
; to the Scheme argument locations
(do-stack (indirect-result-to-pointer result-type arg-type*)
gp-reg-count fp-reg-count int-reg-offset float-reg-offset stack-arg-offset
synthesize-first-argument? varargs? return-space-offset)
synthesize-first-argument? varargs-after return-space-offset)
get-result
(lambda ()
(in-context Tail

View File

@ -8934,10 +8934,14 @@
[else ($oops '$fp-type->pred "unrecognized type ~s" type)])])))
(define $filter-conv
(lambda (who conv*)
(lambda (who conv* num-args)
(define squawk
(lambda (x)
(syntax-error x (format "invalid ~s convention" who))))
(define check-arg-count
(lambda (n orig-c)
(unless (<= n num-args)
(syntax-error orig-c (format "invalid ~s convention with ~a arguments" who num-args)))))
(let loop ([conv* conv*] [selected #f] [accum '()] [keep-accum '()])
(cond
[(null? conv*) (datum->syntax #'filter-conv keep-accum)]
@ -8948,7 +8952,17 @@
(cond
[(not c) (values #f #f)]
[(eq? c '__collect_safe) (values 'adjust-active #f)]
[(eq? c '__varargs) (values 'varargs #f)]
[(eq? c '__varargs)
(check-arg-count 1 orig-c)
(values (cons 'varargs 1) #f)]
[(and (pair? c) (eq? (car c) '__varargs_after)
(pair? (cdr c)) (null? (cddr c))
(let ([i (cadr c)])
(and (integer? i)
(exact? i)
(positive? i))))
(check-arg-count (cadr c) orig-c)
(values (cons 'varargs (cadr c)) #f)]
[else
(values
(case ($target-machine)
@ -8964,7 +8978,8 @@
[else (squawk orig-c)])]
[else (squawk orig-c)])
#t)])])
(when (member c accum)
(when (or (member c accum)
(and (pair? c) (ormap pair? accum)))
(syntax-error orig-c (format "redundant ~s convention" who)))
(when (and select? selected)
(syntax-error orig-c (format "conflicting ~s convention" who)))
@ -8979,16 +8994,18 @@
(define (check-strings-allowed)
(when (memq 'adjust-active (syntax->datum conv*))
($oops who "string argument not allowed with __collect_safe procedure")))
(define (check-floats-allowed)
(when (memq 'varargs (syntax->datum conv*))
($oops who "float argument not allowed for __varargs procedure")))
(define (check-floats-allowed pos)
(let ([va-n (ormap (lambda (conv) (and (pair? conv) (eq? (car conv) 'varargs) (cdr conv)))
(syntax->datum conv*))])
(when (and va-n (>= pos va-n))
($oops who "single-float varargs argument not allowed"))))
(with-syntax ([conv* conv*]
[foreign-name foreign-name]
[?foreign-addr ?foreign-addr]
[(t ...) (generate-temporaries type*)])
(with-syntax ([(((check ...) (actual ...) (arg ...)) ...)
(map
(lambda (type x)
(lambda (type x pos)
(with-syntax ([x x])
(or (case type
[(boolean)
@ -9079,7 +9096,7 @@
(err ($moi) x)))))
(u32*))]
[(single-float)
(check-floats-allowed)
(check-floats-allowed pos)
#f]
[else #f])
(if (or ($ftd? type) ($ftd-as-box? type))
@ -9092,7 +9109,7 @@
#`(#,(if unsafe? #'() #'((unless (pred x) (err ($moi) x))))
(x)
(type)))))))
type* #'(t ...))]
type* #'(t ...) (enumerate type*))]
[(result-filter result)
(case result-type
[(boolean) #`((lambda (x) (not (eq? x 0)))
@ -9160,7 +9177,7 @@
[(_ c ... ?name (arg ...) result)
(lambda (r)
($make-foreign-procedure 'foreign-procedure
($filter-conv 'foreign-procedure #'(c ...))
($filter-conv 'foreign-procedure #'(c ...) (length #'(arg ...)))
(let ([x (datum ?name)]) (and (string? x) x))
#'($foreign-entry ?name)
(map (lambda (x) (filter-type r x #f)) #'(arg ...))
@ -9176,13 +9193,15 @@
(define (check-strings-allowed)
(when (memq 'adjust-active (syntax->datum conv*))
($oops who "string result not allowed with __collect_safe callable")))
(define (check-floats-allowed)
(when (memq 'varargs (syntax->datum conv*))
($oops who "float argument not allowed for __varargs procedure")))
(define (check-floats-allowed pos)
(let ([va-n (ormap (lambda (conv) (and (pair? conv) (eq? (car conv) 'varargs) (cdr conv)))
(syntax->datum conv*))])
(when (and va-n (>= pos va-n))
($oops who "single-float argument not allowed for __varargs procedure"))))
(with-syntax ([conv* conv*] [?proc ?proc])
(with-syntax ([((actual (t ...) (arg ...)) ...)
(map
(lambda (type)
(lambda (type pos)
(or (case type
[(boolean)
(with-syntax ([(x) (generate-temporaries #'(*))])
@ -9269,12 +9288,12 @@
(x)
(unsigned-64)))]
[(single-float)
(check-floats-allowed)
(check-floats-allowed pos)
#f]
[else #f])
(with-syntax ([(x) (generate-temporaries #'(*))])
#`(x (x) (#,(datum->syntax #'foreign-callable type))))))
type*)]
type* (enumerate type*))]
[(result-filter result [extra-arg ...] [extra ...])
(case result-type
[(boolean) #`((lambda (x) (if x 1 0))
@ -9423,7 +9442,7 @@
[(_ c ... ?proc (arg ...) result)
(lambda (r)
($make-foreign-callable 'foreign-callable
($filter-conv 'foreign-callable #'(c ...))
($filter-conv 'foreign-callable #'(c ...) (length #'(arg ...)))
#'?proc
(map (lambda (x) (filter-type r x #f)) #'(arg ...))
(filter-type r #'result #t)))])))