1 ;;;;------------------------------------------------------------------
3 ;;;; Copyright (C) 20012000, 2002, 2004,
4 ;;;; Department of Computer Science, University of Tromso, Norway
6 ;;;; Filename: proglist.lisp
8 ;;;; Author: Frode Vatvedt Fjeld <frodef@acm.org>
9 ;;;; Created at: Mon May 15 13:43:55 2000
10 ;;;; Distribution: See the accompanying file COPYING.
12 ;;;; $Id: proglist.lisp,v 1.6 2004/10/12 09:37:24 ffjeld Exp $
14 ;;;;------------------------------------------------------------------
18 (defvar *label-counter
* 0)
20 (intern (format nil
"~A-~D"
22 (incf *label-counter
*))))
24 (defun labelize-proglist (proglist &optional
(start-address 0))
25 "Destructively modifies the instruction-objects in proglist."
26 (let ((end-address (+ start-address
27 (loop for i in proglist
28 summing
(imagpart (instruction-original-datum i
)))))
29 (label-hash (make-hash-table :test
#'eql
:size
19)))
30 (loop for i in proglist
31 with pc
= start-address
32 do
(incf pc
(imagpart (instruction-original-datum i
)))
33 do
(loop for operands on
(instruction-operands i
)
34 when
(typep (car operands
) 'operand-rel-pointer
)
35 do
(let ((address (+ pc
(slot-value (car operands
) 'offset
))))
36 (if (<= start-address address end-address
)
37 (let ((label (or (gethash address label-hash
)
38 (setf (gethash address label-hash
)
41 (make-instance 'operand-label
'label label
)))
43 (make-instance 'operand-number
'number address
))))))
44 (loop for i in proglist
45 with pc
= start-address
46 when
(gethash pc label-hash
)
47 collect
(gethash pc label-hash
)
49 do
(incf pc
(imagpart (instruction-original-datum i
))))))
51 (defclass forward-reference
()
52 ((labels :initarg labels
:reader forward-reference-labels
)
53 (referring-pc :initarg referring-pc
)
54 (placeholder-cons :initarg placeholder-cons
)
55 (assumed-length :initarg assumed-length
56 :accessor forward-reference-assumed-length
)))
58 (defclass forward-reference-instruction
(forward-reference)
59 ((instruction :initarg instruction
)))
61 (defclass forward-reference-inline-data
(forward-reference)
62 ((inline-data :initarg inline-data
)))
64 (defmethod print-object ((obj forward-reference
) stream
)
65 (format stream
"<unresolved: ~A>" (forward-reference-labels obj
)))
67 (define-condition assumption-failed
()
68 ((forward-reference :initarg forward-reference
69 :reader assumption-failed-forward-reference
)
70 (actual-symtab :initarg actual-symtab
71 :reader assumption-failed-actual-symtab
)
72 (actual-length :initarg actual-length
73 :reader assumption-failed-actual-length
))
75 (lambda (condition stream
)
76 (with-slots (forward-reference actual-symtab assumed-length actual-length
)
78 (with-slots (referring-pc assumed-length
)
81 "Assumption failed when ~A implied length ~A while ~A was assumed."
82 actual-symtab actual-length assumed-length
))))))
84 (defun try-resolve-forward-reference (fwd-to-resolve env optimize-teo
)
85 (if (notevery #'(lambda (label)
86 (symtab-try-lookup-label (assemble-env-symtab env
) label
))
87 (forward-reference-labels fwd-to-resolve
))
88 nil
; this label doesn't (completely) resolve this fwd.
89 (prog1 t
(resolve-forward-reference fwd-to-resolve env optimize-teo
))))
91 (defmethod resolve-forward-reference ((fwd-to-resolve forward-reference-instruction
) env optimize-teo
)
92 (with-slots (labels instruction referring-pc
93 placeholder-cons assumed-length
)
95 ;;; (warn "Resolving at ~D from ~D: ~S" referring-pc (assemble-env-current-pc env)
96 ;;; (mapcar #'(lambda (l)
97 ;;; (cons l (format nil "~D" (symtab-lookup-label (assemble-env-symtab env) l))))
99 (let ((cdatum (instruction-encode instruction
100 (make-assemble-env :symtab
(assemble-env-symtab env
)
101 :current-pc referring-pc
)
103 (when (< (imagpart cdatum
) assumed-length
)
105 (instruction-encode instruction
106 (make-assemble-env :symtab
(assemble-env-symtab env
)
107 :current-pc referring-pc
)
108 #'(lambda (teo-list instr env
)
109 (or (find-if #'(lambda (teo)
111 (template-instr-and-prefix-length
115 (error "Unable to find encoding matching size ~D for ~S"
116 assumed-length instr
))))))
117 (unless (= (imagpart cdatum
) assumed-length
)
118 (error 'assumption-failed
119 'forward-reference fwd-to-resolve
120 'actual-symtab
(mapcar #'(lambda (label)
122 (symtab-lookup-label (assemble-env-symtab env
)
125 'actual-length
(imagpart cdatum
)))
126 (setf (car placeholder-cons
) cdatum
))))
128 (defmethod resolve-forward-reference ((fwd-to-resolve forward-reference-inline-data
) env optimize-teo
)
129 (declare (ignore optimize-teo
))
130 (with-slots (labels inline-data referring-pc placeholder-cons assumed-length
)
132 (let* ((cdatums (reverse (inline-data-encode inline-data env
)))
133 (total-length (loop for cd in cdatums summing
(imagpart cd
))))
134 (unless (= total-length assumed-length
)
135 (error 'assumption-failed
136 'forward-reference fwd-to-resolve
137 'actual-symtab
(mapcar #'(lambda (label)
139 (symtab-lookup-label (assemble-env-symtab env
) label
)))
141 'actual-length total-length
))
142 ;; splice in the cdatums list
143 (setf (car placeholder-cons
) (car cdatums
)
144 (cdr (last cdatums
)) (cdr placeholder-cons
)
145 (cdr placeholder-cons
) (cdr cdatums
)))))
148 (defun guess-next-instruction-length (expr missing-labels program-rest env
)
149 (declare (special *proglist-minimum-expr-size
*))
150 ;; (let ((minimum-size (max previous-length (gethash expr *proglist-minimum-expr-size*))))
151 (or (instruction-user-size expr
)
152 (car (or (gethash expr
*proglist-minimum-expr-size
*)
153 (setf (gethash expr
*proglist-minimum-expr-size
*)
155 ((or ia-x86-instr
::jmp
#+ignore ia-x86-instr
::jcc
)
156 ;; educated guess for jumps..
157 (assert (= 1 (length missing-labels
)))
158 (let ((instruction-offset (position (first missing-labels
) program-rest
)))
159 (assert instruction-offset
()
160 "Can't find label ~S for instruction ~S." (first missing-labels
) expr
)
163 (if (>= instruction-offset
60)
166 #+ignore
(ia-x86-instr::jcc
167 (if (>= instruction-offset
50)
168 (if (eq *cpu-mode
* :32-bit
) '(6 2 4 5) '(4 2 6 5))
169 (if (eq *cpu-mode
* :32-bit
) '(2 6 4) '(2 4 6)))))))
170 (t (loop with guesses
= nil
171 for template in
(templates-lookup-by-class-name (type-of expr
))
172 when
(template-match-by-operand-classes template
(instruction-operands expr
))
173 do
(let ((l (template-instr-and-prefix-length template expr env
)))
174 (unless (member l guesses
)
176 (merge 'list guesses
(list l
) #'<))))
177 finally
(return guesses
)))))))))
179 (defun proglist-encode-internal (prog-list env forward-references encoded-proglist-reverse
182 (declare (special *proglist-minimum-expr-size
*))
183 (loop for expr-rest on prog-list
184 do
(let ((expr (first expr-rest
)))
187 (setf (assemble-env-symtab env
)
188 (symtab-def-label (assemble-env-symtab env
)
190 (assemble-env-current-pc env
)))
191 (loop for fwd in forward-references
192 when
(try-resolve-forward-reference fwd env optimize-teo
)
193 collect fwd into resolved-forwards
194 finally
(setf forward-references
195 (set-difference forward-references resolved-forwards
))))
197 (loop for cbyte in
(create-alignment expr
(assemble-env-current-pc env
))
198 do
(push cbyte encoded-proglist-reverse
)
199 do
(incf (assemble-env-current-pc env
)
202 ;; this follows pretty much the same structure as the INSTRUCTION case.
204 (loop for cbyte in
(inline-data-encode expr env
)
205 do
(push cbyte encoded-proglist-reverse
)
206 do
(incf (assemble-env-current-pc env
)
208 (unresolved-labels (ul-condition)
209 (push 'unresolved-inline-data-forward-reference encoded-proglist-reverse
)
210 (let* ((assumed-length (inline-data-guess-sizeof expr env
))
211 (fwd (make-instance 'forward-reference-inline-data
212 'labels
(unresolved-labels-labels ul-condition
)
214 'referring-pc
(assemble-env-current-pc env
)
215 'assumed-length assumed-length
216 'placeholder-cons encoded-proglist-reverse
)))
219 (return-from proglist-encode-internal
220 (proglist-encode-internal (rest expr-rest
)
222 :symtab
(symtab-add-frame (assemble-env-symtab env
))
223 :current-pc
(+ (assemble-env-current-pc env
)
224 (forward-reference-assumed-length fwd
)))
225 (cons fwd forward-references
)
226 encoded-proglist-reverse
228 (assumption-failed (af-condition)
229 (unless (eq fwd
(assumption-failed-forward-reference af-condition
))
230 (error af-condition
)) ; decline
231 ;; (warn af-condition)
232 (setf (forward-reference-assumed-length fwd
)
233 (assumption-failed-actual-length af-condition
))
234 ;; The assumption failed, we'll retry with the length found at the
235 ;; time the label was actually resolved.
239 (let ((datum (instruction-encode expr env optimize-teo
)))
240 (push datum encoded-proglist-reverse
)
241 (incf (assemble-env-current-pc env
)
243 (unresolved-labels (ul-condition)
244 ;; we stumbled upon a label-reference that wasn't immediately
245 ;; resolvable. We assume it's a forward reference.
246 ;; First, reserve a cons-cell for this instruction to be encoded later
247 (push 'unresolved-forward-reference encoded-proglist-reverse
)
248 ;; Now loop over the possible octet-lengths for this instruction.
249 ;; For each iteration, assume that octet-length, and if no exception is
250 ;; raised, the assumption holds and we break out of the loop with
251 ;; the RETURN-FROM form.
252 (loop for assumed-instr-length
=
253 (guess-next-instruction-length expr
254 (unresolved-labels-labels ul-condition
)
258 #+ignore
(warn "Trying for ~A at ~D with ~A octets.."
259 expr
(assemble-env-current-pc env
) assumed-instr-length
)
260 (let ((fwd (make-instance 'forward-reference-instruction
261 'labels
(unresolved-labels-labels ul-condition
)
263 'referring-pc
(assemble-env-current-pc env
)
264 'assumed-length assumed-instr-length
265 'placeholder-cons encoded-proglist-reverse
)))
267 ;; if assumption holds, break out of the loop
268 (return-from proglist-encode-internal
269 (proglist-encode-internal ; attempt to continue by recursion
272 :symtab
(symtab-add-frame (assemble-env-symtab env
))
273 :current-pc
(+ (assemble-env-current-pc env
)
274 assumed-instr-length
))
275 (cons fwd forward-references
)
276 encoded-proglist-reverse
278 (assumption-failed (af-condition)
279 (unless (eq fwd
(assumption-failed-forward-reference af-condition
))
280 (error af-condition
)) ; decline
281 ;; (warn "~A" af-condition)
282 ;; pop this length off the list of instr-length guesses
283 (assert (gethash expr
*proglist-minimum-expr-size
*) (expr)
284 "Unable to encode ~A. Is the label too far away?" expr
)
285 (pop (gethash expr
*proglist-minimum-expr-size
*))
286 ;; the assumption failed.
287 ;; now continue the dolist loop
289 (error "Unable to encode ~A. Is the label too far away? ~
290 [Should _really_ never get here!!]"
292 ;; When we get here, the whole proglist is encoded.
293 (unless (null forward-references
)
294 (error "There were unresolved forward references: ~A"
295 (mapcar #'forward-reference-labels
296 forward-references
)))
298 (values (nreverse encoded-proglist-reverse
)
299 (assemble-env-symtab env
)))
302 (defun cbyte-to-octet-list (cbyte)
304 with value
= (realpart cbyte
)
305 for i from
(1- (imagpart cbyte
)) downto
0
306 collect
(ldb (byte 8 (* 8 i
)) value
)))
308 (defun proglist-encode (result-type cpu-mode start-addr prog-list
309 &key
(optimize-teo #'optimize-teo-smallest-no16
)
311 (let ((*cpu-mode
* cpu-mode
)
312 (*symtab-lookup
* symtab-lookup
)
313 (*proglist-minimum-expr-size
* (make-hash-table :test
#'eq
)))
314 (declare (special *symtab-lookup
* *proglist-minimum-expr-size
*))
315 (multiple-value-bind (encoded-proglist symtab
)
316 (proglist-encode-internal prog-list
317 (make-assemble-env :current-pc start-addr
318 :symtab
(make-symtab))
319 nil nil optimize-teo
)
322 (values encoded-proglist symtab
))
324 (values (mapcan #'cbyte-to-octet-list encoded-proglist
)
327 (let* ((ep-size (loop for cbyte in encoded-proglist
328 summing
(imagpart cbyte
)))
329 (ep-vector (make-array ep-size
330 :element-type
'(unsigned-byte 8)
333 for cbyte in encoded-proglist
335 do
(loop for bp from
(1- (imagpart cbyte
)) downto
0
336 do
(setf (aref ep-vector i
)
337 (ldb (byte 8 (* 8 bp
)) (realpart cbyte
)))
339 finally
(return (values ep-vector symtab
)))))))))
342 (defun print-encoded-proglist (epl &optional
(base-addr 0))
343 (loop for cbyte in epl
344 and counter from
0 by
1
346 do
(format t
"~8,'0X: ~22<~{ ~2,'0X~}~;~> ~4D ~A~%"
348 (cbyte-to-octet-list cbyte
)
350 (apply #'decode-octet-list
(cbyte-to-octet-list cbyte
)))
351 do
(incf pc
(imagpart cbyte
))))
353 (defun octet-list-to-bioctets (octet-list)
354 (loop for oc on octet-list by
#'cddr
355 collect
(let ((msb (first oc
))
356 (lsb (or (second oc
) 0)))
357 (dpb lsb
(byte 8 8) msb
))))