113 lines
3.9 KiB
Racket
113 lines
3.9 KiB
Racket
#lang racket
|
|
|
|
(require "aarch64var.rkt")
|
|
|
|
; the allocation strategy is quite trivial:
|
|
; always use stack to store results of operations and retrieve operands
|
|
; use temporary registers starting from x0 to perform the operation
|
|
; note that this separation is necessary because ARM is a load/store ISA
|
|
; x0 is used to store the returned value as per Aarch64 calling convention,
|
|
; this is already handled by instruction selection
|
|
|
|
|
|
; allocate-regs-std-instr
|
|
; this procedure should only be applied to: Add, Sub, RetDefault, Svc, Bl etc
|
|
; Use allocate-regs-lmov-instr for LMov
|
|
|
|
; stack-map is a map with keys being the variables
|
|
; and values the position relative to stack base (frame pointer?)
|
|
|
|
; stack-top is the offset where an eventual next variable would be
|
|
; allocated into the stack
|
|
; note that this is a virtual offset; sp must by aligned to 16 bytes
|
|
|
|
; returns the generated instructions
|
|
; the stack-map is modified if needed
|
|
;
|
|
; TODO: does it make sense to use create-stack-bindings on instr-args?
|
|
(define (allocate-regs-std-instr! instr stack-map stack-top)
|
|
(begin
|
|
(define instr-dest (AArch64-instr-dest instr))
|
|
(define instr-args (AArch64-instr-args instr))
|
|
(create-stack-bindings! (append instr-args instr-dest) stack-map stack-top)
|
|
(define reg-args (generate-regs 0 (length instr-args)))
|
|
(define reg-dest (generate-regs (length instr-args) (length instr-dest)))
|
|
(append (generate-ldr instr-args stack-map)
|
|
(list (AArch64-instr-from-dest-args instr reg-dest reg-args))
|
|
(if (empty? instr-dest)
|
|
'()
|
|
(generate-str (generate-reg-n (length instr-args))
|
|
(car instr-dest)
|
|
stack-map)))))
|
|
|
|
(define (generate-regs start len)
|
|
(map generate-reg-n
|
|
(range start (+ start len))))
|
|
|
|
(define (generate-reg-n n)
|
|
(Reg (string->symbol (string-append "x" (number->string n)))))
|
|
|
|
(define (generate-ldr var-list stack-map)
|
|
(for/list ([var var-list]
|
|
[reg (generate-regs 0 (length var-list))])
|
|
(Ldr (AMOffset 'sp (hash-ref stack-map (Var-var-name var))) reg)))
|
|
|
|
(define (generate-str reg dest-var stack-map)
|
|
(list
|
|
(Str reg (AMOffset 'sp (hash-ref stack-map (Var-var-name dest-var))))))
|
|
|
|
|
|
; generate stack read/writes and allocate registers for
|
|
; a LMov instruction
|
|
;(define (allocate-regs-lmov-instr! instr stack-map))
|
|
|
|
; create bindings for the operands that are Var
|
|
(define (create-stack-bindings! operand-list stack-map stack-top)
|
|
(begin
|
|
(for-each (lambda (op)
|
|
(match op
|
|
[(Var var-name)
|
|
(begin
|
|
(create-stack-binding! var-name stack-map stack-top))]
|
|
[_ '()]))
|
|
operand-list)))
|
|
|
|
; modify the stack map if the variable is not there yet
|
|
; only works for 64 bit integers
|
|
(define (create-stack-binding! var-name stack-map stack-top)
|
|
(begin
|
|
(hash-update! stack-map
|
|
var-name
|
|
identity
|
|
stack-top)
|
|
(set! stack-top (- stack-top 8))))
|
|
|
|
(define (AArch64-instr-dest instr)
|
|
(match instr
|
|
[(? LMov?) (list (LMov-dest instr))]
|
|
[(? Add?) (list (Add-dest instr))]
|
|
[(? Sub?) (list (Sub-dest instr))]
|
|
[(? RetDefault?) (list)]))
|
|
|
|
(define (AArch64-instr-args instr)
|
|
(match instr
|
|
[(? LMov?) (list (LMov-src instr))]
|
|
[(Add op1 op2 _) (list op1 op2)]
|
|
[(Sub op1 op2 _) (list op1 op2)]
|
|
[(? RetDefault?) (list)]))
|
|
|
|
(define (AArch64-instr-from-dest-args instr dest args)
|
|
(let ([constructor-args
|
|
(match instr
|
|
[(? LMov?) (append args dest)]
|
|
[(? Add?) (append args dest)]
|
|
[(? Sub?) (append args dest)]
|
|
[(? RetDefault?) (list)])])
|
|
(construct-new-instr instr constructor-args)))
|
|
|
|
(define (construct-new-instr instr args)
|
|
(let*-values ([(struct-type _) (struct-info instr)]
|
|
[(constructor) (struct-type-make-constructor struct-type)])
|
|
(apply constructor args)))
|
|
|