|
1 | 1 | #lang scribble/manual |
| 2 | +@(require (for-label (except-in racket compile ...) a86)) |
2 | 3 | @(require "defns.rkt") |
3 | 4 | @(require "notes/ev.rkt") |
| 5 | +@(require "fancyverb.rkt") |
4 | 6 |
|
5 | 7 | @title[#:style '(unnumbered)]{Project} |
6 | 8 |
|
@@ -279,6 +281,127 @@ to use @racket['rax] for the common case of a single result.) The |
279 | 281 | solution for this problem with function parameters was to use the |
280 | 282 | stack and a similar approach can work for results too. |
281 | 283 |
|
| 284 | + |
| 285 | +@subsection{Returning multiple values to the run-time system or @racket[asm-interp]} |
| 286 | + |
| 287 | +In implementing @racket[values], there are two design decisions you |
| 288 | +have to make: |
| 289 | + |
| 290 | +@itemlist[#:style 'ordered |
| 291 | +@item{How are values going to be represented during the execution of a program?} |
| 292 | +@item{How are values going to be communicated back to the run-time system and/or asm-interp when the program completes?} |
| 293 | +] |
| 294 | + |
| 295 | +The answers to (1) and (2) don't necessarily have to be the same. |
| 296 | + |
| 297 | +Note that you can go a long way working on (1) without making any |
| 298 | +changes to the run-time system or @tt{unload-bits-asm.rkt} (which is |
| 299 | +how the result of @racket[asm-interp] is converted back to a Racket |
| 300 | +value). You can basically punt on (2) and work on (1) by writing |
| 301 | +tests that use multiple values within a computation, but ultimately |
| 302 | +return a single value, e.g. @racket[(let-values ([(x y) (values 1 2)] |
| 303 | +(cons x y)))]. |
| 304 | + |
| 305 | +As for (2), here is a suggestion that you are free to adopt, although |
| 306 | +you can implement (2) however you'd like so long as when running an |
| 307 | +executable that returns multiple values it prints the results in a way |
| 308 | +consistent with how Racket prints and that if using |
| 309 | +@racket[asm-interp], your version of @racket[unload/free] produces |
| 310 | +multiple values whenever the program does. |
| 311 | + |
| 312 | +You can return a vector of results at the end of @racket[entry]. This |
| 313 | +means after the instructions for the program, whatever values are |
| 314 | +produced are converted from the internal representation of values |
| 315 | +(i.e., your design for (1)) to a vector and the address (untagged) is |
| 316 | +put into @tt{rax} to be returned to the run-time system and/or |
| 317 | +@racket[asm-interp]. |
| 318 | + |
| 319 | +Now both the run-time system and @tt{unload-bits-asm.rkt} need to be |
| 320 | +updated to deal with this change in representation for the result. |
| 321 | + |
| 322 | +In @tt{main.c}, the part that gets the result and prints it: |
| 323 | + |
| 324 | +@fancy-c[ |
| 325 | +#<<HERE |
| 326 | + val_t result = entry(heap); |
| 327 | + print_result(result); |
| 328 | + if (val_typeof(result) != T_VOID) |
| 329 | + putchar('\n'); |
| 330 | +HERE |
| 331 | +] |
| 332 | + |
| 333 | +can be changed to getting the vector and printing each element: |
| 334 | + |
| 335 | +@fancy-c[ |
| 336 | +#<<HERE |
| 337 | + val_vect_t *result = entry(heap); |
| 338 | + for (int i = 0; i < result->len; ++i) { |
| 339 | + print_result(result->elems[i]); |
| 340 | + if (val_typeof(result->elems[i]) != T_VOID) |
| 341 | + putchar('\n'); |
| 342 | + } |
| 343 | +HERE |
| 344 | +] |
| 345 | + |
| 346 | +You'll also need to update the signature of @racket[entry] in |
| 347 | +@tt{runtime.h} to: |
| 348 | + |
| 349 | +@fancy-c[" val_vect_t* entry();"] |
| 350 | + |
| 351 | +You'll also need to make a similar change to @racket[unload/free] in |
| 352 | +@tt{unload-bits-asm.rkt}, which plays the role of the run-time system |
| 353 | +when writing tests that use @racket[asm-interp]. |
| 354 | + |
| 355 | +Instead of: |
| 356 | + |
| 357 | +@#reader scribble/comment-reader |
| 358 | +(racketblock |
| 359 | +;; Answer* -> Answer |
| 360 | +(define (unload/free a) |
| 361 | + (match a |
| 362 | + ['err 'err] |
| 363 | + [(cons h v) (begin0 (unload-value v) |
| 364 | + (free h))])) |
| 365 | +) |
| 366 | + |
| 367 | +You'll want: |
| 368 | + |
| 369 | +@#reader scribble/comment-reader |
| 370 | +(racketblock |
| 371 | +;; Answer* -> Answer |
| 372 | +(define (unload/free a) |
| 373 | + (match a |
| 374 | + ['err 'err] |
| 375 | + [(cons h vs) (begin0 (unload-values vs) |
| 376 | + (free h))])) |
| 377 | + |
| 378 | +(define (unload-values vs) |
| 379 | + (let ((vec (unload-value (bitwise-xor vs type-vect)))) |
| 380 | + (apply values (vector->list vec)))) |
| 381 | +) |
| 382 | + |
| 383 | + |
| 384 | + |
| 385 | +Let's say you make these changes to the run-time system and |
| 386 | +@racket[unload/free] before you make any changes to the compiler and |
| 387 | +now you want to adapt the compiler to work with the new set up (before |
| 388 | +trying to do anything with @racket[values]). You can add the following |
| 389 | +at the end of @racket[entry], just before the @racket[(Ret)]: |
| 390 | + |
| 391 | +@#reader scribble/comment-reader |
| 392 | +(racketblock |
| 393 | +;; Create and return unary vector holding the result |
| 394 | +(Mov r8 1) |
| 395 | +(Mov (Offset rbx 0) r8) ; write size of vector, 1 |
| 396 | +(Mov (Offset rbx 8) rax) ; write rax as single element of vector |
| 397 | +(Mov rax rbx) ; return the pointer to the vector |
| 398 | +) |
| 399 | + |
| 400 | +In order to return more values, you'd construct a larger vector. |
| 401 | + |
| 402 | + |
| 403 | + |
| 404 | + |
282 | 405 | @section{Exceptions and exception handling} |
283 | 406 |
|
284 | 407 | Exceptions and exception handling mechanisms are widely used in modern |
|
0 commit comments