about summary refs log blame commit diff stats
path: root/translate_subx_debug
blob: 021de0a435f83de61eb4f26447ea6256ab0752fe (plain) (tree)
1
2
3
4
5
6
7
8
9
         
                                                       

                              




                                                                            
                                                                                

                                                                           
 

                                                                              
                                                                               




                                                                              
                                                
                                               

                                                
                                               

              
#!/bin/sh
# Translate SubX files with debug information on Linux.
#
# Mu's core tooling has a gap:
#   0. The C++ translator 'subx translate' can generate debug information on
#   Linux or BSD or Mac, but doesn't support any syntax sugar.
#   1. The self-hosted translator 'translate' runs in emulated mode and can
#   run on Linux or BSD or Mac. However, syntax sugar passes (sigils and
#   calls) can be very slow to run emulated.
#   2. The self-hosted translator 'translate_subx' runs natively on Linux. It is
#   fast, but you get no trace for runs and zero error-checking on the code
#   emitted by sigils and calls. Which could still be buggy.
#
# This script is a hack to get the best of all worlds. We run natively what we
# must, and leverage as much debug information as possible. This arrangement
# is snappy but requires Linux just like 'translate_subx'. You also are on your
# own to mentally map desugared instructions in traces and error messages back
# to the original sources.

set -e

cat $*          |apps/braces          > a.braces
cat a.braces    |apps/calls           > a.calls
cat a.calls     |apps/sigils          > a.sigils

./bootstrap --debug translate a.sigils -o a.elf

chmod +x a.elf
37'>337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377







































                                                                                                                                                                             
                                  




                                                                                                                                                                              
                                                            




                                                                                                                                                                                        
                                                    
                   

                                                                                                                                                                              
                                                                                                                                                                            




















                                                                                                                                                                                 










































































































































































































































































































                                                                                                                                                                              
# Tokenize by whitespace.

== code
#   instruction                     effective address                                                   register    displacement    immediate
# . op          subop               mod             rm32          base        index         scale       r32
# . 1-3 bytes   3 bits              2 bits          3 bits        3 bits      3 bits        2 bits      2 bits      0/1/2/4 bytes   0/1/2/4 bytes

# (re)compute the bounds of the next word in the line (surrounded by whitespace,
# treating '#' comments as a single word)
# return empty string on reaching end of file
next-word:  # line: (addr stream byte), out: (addr slice)
    # . prologue
    55/push-ebp
    89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    # . save registers
    50/push-eax
    51/push-ecx
    56/push-esi
    57/push-edi
    # esi = line
    8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
    # edi = out
    8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   0xc/disp8       .                 # copy *(ebp+12) to edi
    # skip-chars-matching-whitespace(line)
    # . . push args
    ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    # . . call
    e8/call  skip-chars-matching-whitespace/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
$next-word:check0:
    # if (line->read >= line->write) clear out and return
    # . eax = line->read
    8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   4/disp8         .                 # copy *(esi+4) to eax
    # . if (eax < line->write) goto next check
    3b/compare                      0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # compare eax with *esi
    7c/jump-if-<  $next-word:check-for-comment/disp8
    # . return out
    c7          0/subop/copy        0/mod/direct    7/rm32/edi    .           .             .           .           .               0/imm32           # copy to *edi
    c7          0/subop/copy        1/mod/*+disp8   7/rm32/edi    .           .             .           .           4/disp8         0/imm32           # copy to *(edi+4)
    e9/jump  $next-word:end/disp32
$next-word:check-for-comment:
    # out->start = &line->data[line->read]
    8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(esi+4) to ecx
    8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/esi  1/index/ecx   .           0/r32/eax   0xc/disp8       .                 # copy esi+ecx+12 to eax
    89/copy                         0/mod/indirect  7/rm32/edi    .           .             .           0/r32/eax   .               .                 # copy eax to *edi
    # if (line->data[line->read] == '#') return rest of line
    # . eax = line->data[line->read]
    31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
    8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    6/base/esi  1/index/ecx   .           0/r32/AL    0xc/disp8       .                 # copy byte at *(esi+ecx+12) to AL
    # . compare
    3d/compare-eax-and  0x23/imm32/pound
    0f 85/jump-if-!=  $next-word:regular-word/disp32
$next-word:comment:
    # out->end = out->start
    8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/esi  1/index/ecx   .           0/r32/eax   0xc/disp8       .                 # copy esi+ecx+12 to eax
    89/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           0/r32/eax   4/disp8         .                 # copy eax to *(edi+4)
    # var write/ecx: int = line->write
    8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy *esi to ecx
$next-word:comment-loop:
    # if (line->read >= line->write) break
    39/compare                      1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # compare *(esi+4) with ecx
    0f 8d/jump-if->=  $next-word:comment-break/disp32
    # ++line->read
    ff          0/subop/increment   1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # increment *(esi+4)
    # ++out->end
    ff          0/subop/increment   1/mod/*+disp8   7/rm32/edi    .           .             .           .           4/disp8         .                 # increment *(edi+4)
    # if (*out->end == newline) break
    8b/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           0/r32/eax   4/disp8         .                 # copy *(edi+4) to eax
    8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           0/r32/AL    .               .                 # copy byte at *eax to AL
    25/and-eax-with  0xff/imm32
    3d/compare-eax-and  0xa/imm32/newline
    0f 84/jump-if-=  $next-word:comment-break/disp32
    # loop
    e9/jump  $next-word:comment-loop/disp32
$next-word:comment-break:
    # return
    e9/jump  $next-word:end/disp32
$next-word:regular-word:
    # otherwise skip-chars-not-matching-whitespace(line)  # including trailing newline
    # . . push args
    ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    # . . call
    e8/call  skip-chars-not-matching-whitespace/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    # out->end = &line->data[line->read]
    8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(esi+4) to ecx
    8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/esi  1/index/ecx   .           0/r32/eax   0xc/disp8       .                 # copy esi+ecx+12 to eax
    89/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           0/r32/eax   4/disp8         .                 # copy eax to *(edi+4)
$next-word:end:
    # . restore registers
    5f/pop-to-edi
    5e/pop-to-esi
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    5d/pop-to-ebp
    c3/return

test-next-word:
    # . prologue
    55/push-ebp
    89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    # setup
    # . clear-stream(_test-stream)
    # . . push args
    68/push  _test-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    # var slice/ecx: slice
    68/push  0/imm32/end
    68/push  0/imm32/start
    89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
    # write(_test-stream, "  ab")
    # . . push args
    68/push  "  ab"/imm32
    68/push  _test-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    # next-word(_test-stream, slice)
    # . . push args
    51/push-ecx
    68/push  _test-stream/imm32
    # . . call
    e8/call  next-word/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    # check-ints-equal(slice->start - _test-stream->data, 2, msg)
    # . check-ints-equal(slice->start - _test-stream, 14, msg)
    # . . push args
    68/push  "F - test-next-word: start"/imm32
    68/push  0xe/imm32
    # . . push slice->start - _test-stream
    8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
    81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-stream/imm32 # subtract from eax
    50/push-eax
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    # check-ints-equal(slice->end - _test-stream->data, 4, msg)
    # . check-ints-equal(slice->end - _test-stream, 16, msg)
    # . . push args
    68/push  "F - test-next-word: end"/imm32
    68/push  0x10/imm32
    # . . push slice->end - _test-stream
    8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
    81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-stream/imm32 # subtract from eax
    50/push-eax
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    # . epilogue
    89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    5d/pop-to-ebp
    c3/return

test-next-word-returns-whole-comment:
    # . prologue
    55/push-ebp
    89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    # setup
    # . clear-stream(_test-stream)
    # . . push args
    68/push  _test-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    # var slice/ecx: slice
    68/push  0/imm32/end
    68/push  0/imm32/start
    89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
    # write(_test-stream, "  # a")
    # . . push args
    68/push  "  # a"/imm32
    68/push  _test-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    # next-word(_test-stream, slice)
    # . . push args
    51/push-ecx
    68/push  _test-stream/imm32
    # . . call
    e8/call  next-word/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    # check-ints-equal(slice->start - _test-stream->data, 2, msg)
    # . check-ints-equal(slice->start - _test-stream, 14, msg)
    # . . push args
    68/push  "F - test-next-word-returns-whole-comment: start"/imm32
    68/push  0xe/imm32
    # . . push slice->start - _test-stream
    8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
    81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-stream/imm32 # subtract from eax
    50/push-eax
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    # check-ints-equal(slice->end - _test-stream->data, 5, msg)
    # . check-ints-equal(slice->end - _test-stream, 17, msg)
    # . . push args
    68/push  "F - test-next-word-returns-whole-comment: end"/imm32
    68/push  0x11/imm32
    # . . push slice->end - _test-stream
    8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
    81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-stream/imm32 # subtract from eax
    50/push-eax
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    # . epilogue
    89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    5d/pop-to-ebp
    c3/return

test-next-word-returns-empty-string-on-eof:
    # . prologue
    55/push-ebp
    89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    # setup
    # . clear-stream(_test-stream)
    # . . push args
    68/push  _test-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    # var slice/ecx: slice
    68/push  0/imm32/end
    68/push  0/imm32/start
    89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
    # write nothing to _test-stream
    # next-word(_test-stream, slice)
    # . . push args
    51/push-ecx
    68/push  _test-stream/imm32
    # . . call
    e8/call  next-word/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    # check-ints-equal(slice->end - slice->start, 0, msg)
    # . . push args
    68/push  "F - test-next-word-returns-empty-string-on-eof"/imm32
    68/push  0/imm32
    # . . push slice->end - slice->start
    8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
    2b/subtract                     0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract *ecx from eax
    50/push-eax
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    # . epilogue
    89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    5d/pop-to-ebp
    c3/return

test-next-word-returns-empty-string-on-newline:
    # . prologue
    55/push-ebp
    89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    # setup
    # . clear-stream(_test-stream)
    # . . push args
    68/push  _test-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    # var slice/ecx: slice
    68/push  0/imm32/end
    68/push  0/imm32/start
    89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
    # write some whitespace and a newline
    # . . push args
    68/push  "  \n"/imm32
    68/push  _test-stream/imm32
    # . . call
    e8/call  write/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    # next-word(_test-stream, slice)
    # . . push args
    51/push-ecx
    68/push  _test-stream/imm32
    # . . call
    e8/call  next-word/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    # check-ints-equal(slice->end - slice->start, 0, msg)
    # . . push args
    68/push  "F - test-next-word-returns-empty-string-on-newline"/imm32
    68/push  0/imm32
    # . . push slice->end - slice->start
    8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
    2b/subtract                     0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract *ecx from eax
    50/push-eax
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    # . epilogue
    89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    5d/pop-to-ebp
    c3/return

# (re)compute the bounds of the next word in the line (separated by whitespace)
# return empty string on reaching end of file
next-raw-word:  # line: (addr stream byte), out: (addr slice)
    # . prologue
    55/push-ebp
    89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    # . save registers
    50/push-eax
    51/push-ecx
    56/push-esi
    57/push-edi
    # esi = line
    8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
    # edi = out
    8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   0xc/disp8       .                 # copy *(ebp+12) to edi
    # skip-chars-matching-whitespace(line)
    # . . push args
    ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    # . . call
    e8/call  skip-chars-matching-whitespace/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
$next-raw-word:check0:
    # if (line->read >= line->write) clear out and return
    # . eax = line->read
    8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   4/disp8         .                 # copy *(esi+4) to eax
    # . if (eax < line->write) goto next check
    3b/compare                      0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # compare eax with *esi
    7c/jump-if-<  $next-raw-word:word-exists/disp8
    # . return out
    c7          0/subop/copy        0/mod/direct    7/rm32/edi    .           .             .           .           .               0/imm32           # copy to *edi
    c7          0/subop/copy        1/mod/*+disp8   7/rm32/edi    .           .             .           .           4/disp8         0/imm32           # copy to *(edi+4)
    eb/jump  $next-raw-word:end/disp8
$next-raw-word:word-exists:
    # out->start = &line->data[line->read]
    8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(esi+4) to ecx
    8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/esi  1/index/ecx   .           0/r32/eax   0xc/disp8       .                 # copy esi+ecx+12 to eax
    89/copy                         0/mod/indirect  7/rm32/edi    .           .             .           0/r32/eax   .               .                 # copy eax to *edi
    # skip-chars-not-matching-whitespace(line)  # including trailing newline
    # . . push args
    ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    # . . call
    e8/call  skip-chars-not-matching-whitespace/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    # out->end = &line->data[line->read]
    8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(esi+4) to ecx
    8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/esi  1/index/ecx   .           0/r32/eax   0xc/disp8       .                 # copy esi+ecx+12 to eax
    89/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           0/r32/eax   4/disp8         .                 # copy eax to *(edi+4)
$next-raw-word:end:
    # . restore registers
    5f/pop-to-edi
    5e/pop-to-esi
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    5d/pop-to-ebp
    c3/return