From f3901d906901e80588f2e3e56bbe3052e47e4d64 Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Sun, 12 Aug 2018 21:04:14 -0700 Subject: 4512 - divide labels into two categories Targets you can jump to and ones you can call are conceptually disjoint sets. I'm highlighting these in Vim, but it's a pretty complex pattern. Arguably errors shouldn't be highlighted. Only warnings that are easy to be accidentally deployed. --- subx/035labels.cc | 19 +++++++++---------- subx/037label_types.cc | 45 +++++++++++++++++++++++++++++++++++++++++++++ subx/ex3.subx | 8 ++++---- subx/ex7.subx | 4 ++-- subx/subx.vim | 7 +++++++ subx/vim_errors.subx | 34 ++++++++++++++++++++++++++++++++++ 6 files changed, 101 insertions(+), 16 deletions(-) create mode 100644 subx/037label_types.cc create mode 100644 subx/vim_errors.subx diff --git a/subx/035labels.cc b/subx/035labels.cc index db54d329..c7274613 100644 --- a/subx/035labels.cc +++ b/subx/035labels.cc @@ -53,7 +53,6 @@ loop: :(before "End Level-2 Transforms") Transform.push_back(rewrite_labels); - :(code) void rewrite_labels(program& p) { trace(99, "transform") << "-- rewrite labels" << end(); @@ -190,19 +189,19 @@ string drop_last(const string& s) { # 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 # address 1 -loop: -loop2: + $loop: + $loop2: # address 1 (labels take up no space) 05 0x0d0c0b0a/imm32 # add to EAX # address 6 - eb loop2/disp8 + eb $loop2/disp8 # address 8 - eb loop3/disp8 + eb $loop3/disp8 # address 10 -loop3: -+transform: label 'loop' is at address 1 -+transform: label 'loop2' is at address 1 -+transform: label 'loop3' is at address 10 + $loop3: ++transform: label '$loop' is at address 1 ++transform: label '$loop2' is at address 1 ++transform: label '$loop3' is at address 10 # first jump is to -7 +transform: instruction after transform: 'eb f9' # second jump is to 0 (fall through) @@ -234,6 +233,6 @@ xz: # instruction effective address operand 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 - -a: # indent to avoid looking like a trace_should_not_contain command + -a: # indent to avoid looking like a trace_should_not_contain command for this scenario 05 0x0d0c0b0a/imm32 # add to EAX +error: '-a' starts with '-', which can be confused with a negative number; use a different name diff --git a/subx/037label_types.cc b/subx/037label_types.cc new file mode 100644 index 00000000..b80db732 --- /dev/null +++ b/subx/037label_types.cc @@ -0,0 +1,45 @@ +//: Distinguish between labels marking the start of a function, and labels +//: inside functions. +//: +//: - Labels within functions start with a '$', and are only permitted in +//: 'jump' instructions. +//: +//: - Labels marking the start of functions lack the '$' sigil, and are only +//: permitted in 'call' instructions. + +:(before "Rewrite Labels(segment code)") +check_label_types(code); +if (trace_contains_errors()) return; +:(code) +void check_label_types(const segment& code) { + trace(99, "transform") << "-- check label types" << end(); + for (int i = 0; i < SIZE(code.lines); ++i) + check_label_types(code.lines.at(i)); +} + +void check_label_types(const line& inst) { + int idx = first_operand(inst); + if (idx >= SIZE(inst.words)) return; + const word& target = inst.words.at(idx); + if (is_number(target.data)) return; // handled elsewhere + if (is_jump(inst) && target.data.at(0) != '$') + raise << "'" << inst.original << "': jumps should always be to internal labels starting with '$'\n" << end(); + if (is_call(inst) && target.data.at(0) == '$') + raise << "'" << inst.original << "': calls should always be to function labels (not starting with '$')\n" << end(); +} + +:(scenario catch_jump_to_function) +% Hide_errors = true; +== 0x1 +main: +7e/jump-if foo/disp8 +foo: ++error: '7e/jump-if foo/disp8': jumps should always be to internal labels starting with '$' + +:(scenario catch_call_to_internal_label) +% Hide_errors = true; +== 0x1 +main: +e8/call $foo/disp32 + $foo: # indent to avoid looking like a trace_count command for this scenario ++error: 'e8/call $foo/disp32': calls should always be to function labels (not starting with '$') diff --git a/subx/ex3.subx b/subx/ex3.subx index eb66f1bf..41d2b94e 100644 --- a/subx/ex3.subx +++ b/subx/ex3.subx @@ -16,18 +16,18 @@ # counter: ECX = 1 b9/copy 1/imm32 # copy 1 to ECX -loop: +$loop: # while (counter <= 10) 81 7/subop/compare 3/mod/direct 1/rm32/ecx 0xa/imm32 # compare ECX, 10/imm - 7f/jump-if exit/disp8 # jump-if-greater exit + 7f/jump-if $exit/disp8 # jump-if-greater $exit # result += counter 01/add 3/mod/direct 3/rm32/ebx 1/r32/ecx # add ECX to EBX # ++counter 81 0/subop/add 3/mod/direct 1/rm32/ecx 1/imm32 # add 1 to ECX # loop - eb/jump loop/disp8 # jump loop + eb/jump $loop/disp8 # jump $loop -exit: +$exit: # exit(EBX) b8/copy 1/imm32 # copy 1 to EAX cd/syscall 0x80/imm8 # int 80h diff --git a/subx/ex7.subx b/subx/ex7.subx index 07b2e016..b386df82 100644 --- a/subx/ex7.subx +++ b/subx/ex7.subx @@ -38,7 +38,7 @@ factorial: b8/copy . . . . . . . 1/imm32 # copy 1 to EAX # if (n <= 1) jump exit 81 7/subop/compare 3/mod/direct 2/rm32/EDX . . . . . 1/imm32 # compare EDX with 1 - 7e/jump-if-<= . . . . . . factorial:exit/disp8 # jump if <= to exit + 7e/jump-if-<= . . . . . . $exit/disp8 # jump if <= to $exit # EBX: n-1 89/copy 3/mod/direct 3/rm32/EBX . . . 2/r32/EDX . . # copy EDX to EBX 81 5/subop/subtract 3/mod/direct 3/rm32/EBX . . . . . 1/imm32 # subtract 1 from EBX @@ -58,7 +58,7 @@ factorial: # return n * factorial(n-1) 0f af/multiply 3/mod/direct 2/rm32/EDX . . . 0/r32/EAX . . # multiply EDX (n) into EAX (factorial(n-1)) # TODO: check for overflow -factorial:exit: +$exit: c3/return # vim:ft=subx:nowrap:so=0 diff --git a/subx/subx.vim b/subx/subx.vim index 17d63932..e6825c72 100644 --- a/subx/subx.vim +++ b/subx/subx.vim @@ -35,4 +35,11 @@ call matchadd("Warn", '\c^\s*7[45cdef].*\<\(0x\)\?[0-9a-f]\+/disp8') " conditio call matchadd("Warn", '\c^\s*eb.*\<\(0x\)\?[0-9a-f]\+/disp16') " unconditional jump disp16 call matchadd("Warn", '\c^\s*0f[^\s]*\s*8[45cdef].*\<\(0x\)\?[0-9a-f]\+/disp16') " conditional jump disp16 +" Mismatch in label type +call matchadd("Error", '\c^\s*e8.*\$') " call +call matchadd("Error", '\c^\s*e9\(/[^ ]*\)\?\s*\(\.\s*\)\+[^\$\. ]\([ \$0-9a-fA-F-]\+\>\)\@!') " unconditional jump disp8 +call matchadd("Error", '\c^\s*7[45cdef]\(/[^ ]*\)\?\s*\(\.\s*\)\+[^\$\. ]\([ \$0-9a-fA-F-]\+\>\)\@!') " conditional jump disp8 +call matchadd("Error", '\c^\s*eb\(/[^ ]*\)\?\s*\(\.\s*\)\+[^\$\. ]\([ \$0-9a-fA-F-]\+\>\)\@!') " unconditional jump disp16 +call matchadd("Error", '\c^\s*0f[^\s]*\s*8[45cdef]\(/[^ ]*\)\?\s*\(\.\s*\)\+[^\$\. ]\([ \$0-9a-fA-F-]\+\>\)\@!') " conditional jump disp16 + let &cpo = s:save_cpo diff --git a/subx/vim_errors.subx b/subx/vim_errors.subx new file mode 100644 index 00000000..02a82f25 --- /dev/null +++ b/subx/vim_errors.subx @@ -0,0 +1,34 @@ +# test file for Vim highlighting + +## unindented lines below should all get Warn highlighting in .subx files +# indented lines should get no highlighting + +# raw displacement in jump/call instructions +e8 10/disp32 +e8 . 10/disp32 +e8/call . -10/disp32 +e8/call . 0x10/disp32 +e9/jump . 10/disp8 +e9/jump . -10/disp8 +e9/jump . f0/disp8 +7c/jump . 10/disp8 +7c/jump . -10/disp8 +eb/jump . 10/disp16 +eb/jump . -10/disp16 +0f/foo 8e/jump . 10/disp16 +0f/foo 8e/jump . -10/disp16 + +## unindented lines below should all get Error highlighting in .subx files +# indented lines should get no highlighting + +# mismatch in label type (call to '$' or jump to non-'$') +e8 $foo/disp32 +e8 . $foo/disp32 +e8/call . $foo/disp32 +e8/call . $foo/disp32 +e9/jump . foo/disp8 + e9/jump . $foo/disp8 + e9/jump . . $foo/disp8 +7c/jump . foo/disp8 +eb/jump . foo/disp16 +0f/foo 8e/jump . foo/disp16 -- cgit 1.4.1-2-gfad0