From 6c5fc43f758e7c3590d058f9ce18ef9378c95ec4 Mon Sep 17 00:00:00 2001 From: David Van Horn Date: Mon, 31 Oct 2022 14:33:02 -0400 Subject: [PATCH 1/2] Check immediate ranges. Addresses much of #115. --- langs/a86/ast.rkt | 29 ++++++++++++++++++++++------- langs/a86/test/errors.rkt | 18 ++++++++++++++++++ www/notes/a86.scrbl | 38 ++++++++++++++++++++++++++++++-------- 3 files changed, 70 insertions(+), 15 deletions(-) diff --git a/langs/a86/ast.rkt b/langs/a86/ast.rkt index 50f7987d..38928f3a 100644 --- a/langs/a86/ast.rkt +++ b/langs/a86/ast.rkt @@ -35,6 +35,8 @@ (error n "expects register; given ~v" a1)) (unless (or (exact-integer? a2) (register? a2) (offset? a2)) (error n "expects exact integer, register, or offset; given ~v" a2)) + (when (and (exact-integer? a2) (> (integer-length a2) 32)) + (error n "literal must not exceed 32-bits; given ~v (~v bits); go through a register instead" a2 (integer-length a2))) (values a1 a2))) (define check:register @@ -43,7 +45,7 @@ (error n "expects register; given ~v" a1)) a1)) -(define check:src-dest +(define (check:src-dest width) (λ (a1 a2 n) (unless (or (register? a1) (offset? a1)) (error n "expects register or offset; given ~v" a1)) @@ -51,10 +53,13 @@ (error n "expects register, offset, exact integer, or defined constant; given ~v" a2)) (when (and (offset? a1) (offset? a2)) (error n "cannot use two memory locations; given ~v, ~v" a1 a2)) + (when (and (exact-integer? a2) (> (integer-length a2) width)) + (error n "literal must not exceed ~v-bits; given ~v (~v bits); go through a register instead" width a2 (integer-length a2))) (when (and (offset? a1) (exact-integer? a2)) (error n "cannot use a memory locations and literal; given ~v, ~v; go through a register instead" a1 a2)) (values a1 a2))) + (define check:shift (λ (a1 a2 n) (unless (register? a1) @@ -76,6 +81,8 @@ (λ (a1 n) (unless (or (exact-integer? a1) (register? a1)) (error n "expects exact integer or register; given ~v" a1)) + (when (and (exact-integer? a1) (> (integer-length a1) 32)) + (error n "literal must not exceed 32-bits; given ~v (~v bits); go through a register instead" a1 (integer-length a1))) a1)) (define check:lea @@ -126,10 +133,10 @@ (instruct Label (x) check:label-symbol) (instruct Call (x) check:target) (instruct Ret () check:none) -(instruct Mov (dst src) check:src-dest) +(instruct Mov (dst src) (check:src-dest 64)) (instruct Add (dst src) check:arith) (instruct Sub (dst src) check:arith) -(instruct Cmp (a1 a2) check:src-dest) +(instruct Cmp (a1 a2) (check:src-dest 32)) (instruct Jmp (x) check:target) (instruct Je (x) check:target) (instruct Jne (x) check:target) @@ -137,9 +144,9 @@ (instruct Jle (x) check:target) (instruct Jg (x) check:target) (instruct Jge (x) check:target) -(instruct And (dst src) check:src-dest) -(instruct Or (dst src) check:src-dest) -(instruct Xor (dst src) check:src-dest) +(instruct And (dst src) (check:src-dest 32)) +(instruct Or (dst src) (check:src-dest 32)) +(instruct Xor (dst src) (check:src-dest 32)) (instruct Sal (dst i) check:shift) (instruct Sar (dst i) check:shift) (instruct Push (a1) check:push) @@ -169,7 +176,7 @@ (symbol? x) (integer? x))) -(provide offset? register? instruction? label?) +(provide offset? register? instruction? label? 64-bit-integer? 32-bit-integer?) (define offset? Offset?) @@ -177,6 +184,14 @@ (and (memq x '(cl eax rax rbx rcx rdx rbp rsp rsi rdi r8 r9 r10 r11 r12 r13 r14 r15)) #t)) +(define (64-bit-integer? x) + (and (exact-integer? x) + (<= (integer-length x) 64))) + +(define (32-bit-integer? x) + (and (exact-integer? x) + (<= (integer-length x) 32))) + (define (label? x) (and (symbol? x) (nasm-label? x) diff --git a/langs/a86/test/errors.rkt b/langs/a86/test/errors.rkt index 244c6008..2079eb39 100644 --- a/langs/a86/test/errors.rkt +++ b/langs/a86/test/errors.rkt @@ -2,3 +2,21 @@ (require rackunit "../ast.rkt") (check-exn exn:fail? (thunk (Mov (Offset 'rax 0) 100))) + +;; Checking literal widths +(check-exn exn:fail? (thunk (Mov 'rax (expt 2 64)))) +(check-not-exn (thunk (Mov 'rax (sub1 (expt 2 64))))) +(check-exn exn:fail? (thunk (Cmp 'rax (expt 2 32)))) +(check-not-exn (thunk (Cmp 'rax (sub1 (expt 2 32))))) +(check-exn exn:fail? (thunk (And 'rax (expt 2 32)))) +(check-not-exn (thunk (And 'rax (sub1 (expt 2 32))))) +(check-exn exn:fail? (thunk (Or 'rax (expt 2 32)))) +(check-not-exn (thunk (Or 'rax (sub1 (expt 2 32))))) +(check-exn exn:fail? (thunk (Xor 'rax (expt 2 32)))) +(check-not-exn (thunk (Xor 'rax (sub1 (expt 2 32))))) +(check-exn exn:fail? (thunk (Push (expt 2 32)))) +(check-not-exn (thunk (Push (sub1 (expt 2 32))))) +(check-exn exn:fail? (thunk (Add 'rax (expt 2 32)))) +(check-not-exn (thunk (Add 'rax (sub1 (expt 2 32))))) +(check-exn exn:fail? (thunk (Sub 'rax (expt 2 32)))) +(check-not-exn (thunk (Sub 'rax (sub1 (expt 2 32))))) diff --git a/www/notes/a86.scrbl b/www/notes/a86.scrbl index 3e420a85..e0fa1cb7 100644 --- a/www/notes/a86.scrbl +++ b/www/notes/a86.scrbl @@ -720,6 +720,28 @@ Each register plays the same role as in x86, so for example A predicate for offsets. } +@defproc[(64-bit-integer? [x any/c]) boolean?]{ + A predicate for determining if a value is an integer that fits in 64-bits. + + @ex[ + (64-bit-integer? 0) + (64-bit-integer? (sub1 (expt 2 64))) + (64-bit-integer? (expt 2 64)) + (64-bit-integer? (- (expt 2 63))) + (64-bit-integer? (sub1 (- (expt 2 63))))] +} + +@defproc[(32-bit-integer? [x any/c]) boolean?]{ + A predicate for determining if a value is an integer that fits in 64-bits. + + @ex[ + (32-bit-integer? 0) + (32-bit-integer? (sub1 (expt 2 32))) + (32-bit-integer? (expt 2 32)) + (32-bit-integer? (- (expt 2 32))) + (32-bit-integer? (sub1 (- (expt 2 32))))] +} + @defproc[(seq [x (or/c instruction? (listof instruction?))] ...) (listof instruction?)]{ A convenience function for splicing togeter instructions and lists of instructions. @@ -866,7 +888,7 @@ Each register plays the same role as in x86, so for example } -@defstruct*[Mov ([dst (or/c register? offset?)] [src (or/c register? offset? exact-integer?)])]{ +@defstruct*[Mov ([dst (or/c register? offset?)] [src (or/c register? offset? 64-bit-integer?)])]{ A move instruction. Moves @racket[src] to @racket[dst]. @@ -885,7 +907,7 @@ Each register plays the same role as in x86, so for example } -@defstruct*[Add ([dst register?] [src (or/c register? offset? exact-integer?)])]{ +@defstruct*[Add ([dst register?] [src (or/c register? offset? 32-bit-integer?)])]{ An addition instruction. Adds @racket[src] to @racket[dst] and writes the result to @racket[dst]. @@ -901,7 +923,7 @@ Each register plays the same role as in x86, so for example ] } -@defstruct*[Sub ([dst register?] [src (or/c register? offset? exact-integer?)])]{ +@defstruct*[Sub ([dst register?] [src (or/c register? offset? 32-bit-integer?)])]{ A subtraction instruction. Subtracts @racket[src] frrom @racket[dst] and writes the result to @racket[dst]. @@ -917,7 +939,7 @@ Each register plays the same role as in x86, so for example ] } -@defstruct*[Cmp ([a1 (or/c register? offset?)] [a2 (or/c register? offset? exact-integer?)])]{ +@defstruct*[Cmp ([a1 (or/c register? offset?)] [a2 (or/c register? offset? 32-bit-integer?)])]{ Compare @racket[a1] to @racket[a2]. Doing a comparison sets the status flags that affect the conditional instructions like @racket[Je], @racket[Jl], etc. @@ -1028,7 +1050,7 @@ Each register plays the same role as in x86, so for example ] } -@defstruct*[And ([dst (or/c register? offset?)] [src (or/c register? offset? exact-integer?)])]{ +@defstruct*[And ([dst (or/c register? offset?)] [src (or/c register? offset? 32-bit-integer?)])]{ Compute logical ``and'' of @racket[dst] and @racket[src] and put result in @racket[dst]. @#reader scribble/comment-reader @@ -1043,7 +1065,7 @@ Each register plays the same role as in x86, so for example ) } -@defstruct*[Or ([dst (or/c register? offset?)] [src (or/c register? offset? exact-integer?)])]{ +@defstruct*[Or ([dst (or/c register? offset?)] [src (or/c register? offset? 32-bit-integer?)])]{ Compute logical ``or'' of @racket[dst] and @racket[src] and put result in @racket[dst]. @#reader scribble/comment-reader @@ -1058,7 +1080,7 @@ Each register plays the same role as in x86, so for example ) } -@defstruct*[Xor ([dst (or/c register? offset?)] [src (or/c register? offset? exact-integer?)])]{ +@defstruct*[Xor ([dst (or/c register? offset?)] [src (or/c register? offset? 32-bit-integer?)])]{ Compute logical ``exclusive or'' of @racket[dst] and @racket[src] and put result in @racket[dst]. @#reader scribble/comment-reader @@ -1113,7 +1135,7 @@ Each register plays the same role as in x86, so for example ) } -@defstruct*[Push ([a1 (or/c exact-integer? register?)])]{ +@defstruct*[Push ([a1 (or/c 32-bit-integer? register?)])]{ Decrements the stack pointer and then stores the source operand on the top of the stack. From a520c4524349e99c4a3ca02d6503bc0a3d4c0541 Mon Sep 17 00:00:00 2001 From: David Van Horn Date: Mon, 31 Oct 2022 15:18:23 -0400 Subject: [PATCH 2/2] Fix up error message for mov when imm is too big (suggesting going through a register doesn't make sense; the imm was bigger than 64-bits). --- langs/a86/ast.rkt | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/langs/a86/ast.rkt b/langs/a86/ast.rkt index 38928f3a..26275c86 100644 --- a/langs/a86/ast.rkt +++ b/langs/a86/ast.rkt @@ -45,7 +45,7 @@ (error n "expects register; given ~v" a1)) a1)) -(define (check:src-dest width) +(define check:src-dest (λ (a1 a2 n) (unless (or (register? a1) (offset? a1)) (error n "expects register or offset; given ~v" a1)) @@ -53,12 +53,25 @@ (error n "expects register, offset, exact integer, or defined constant; given ~v" a2)) (when (and (offset? a1) (offset? a2)) (error n "cannot use two memory locations; given ~v, ~v" a1 a2)) - (when (and (exact-integer? a2) (> (integer-length a2) width)) - (error n "literal must not exceed ~v-bits; given ~v (~v bits); go through a register instead" width a2 (integer-length a2))) + (when (and (exact-integer? a2) (> (integer-length a2) 32)) + (error n "literal must not exceed 32-bits; given ~v (~v bits); go through a register instead" a2 (integer-length a2))) (when (and (offset? a1) (exact-integer? a2)) (error n "cannot use a memory locations and literal; given ~v, ~v; go through a register instead" a1 a2)) (values a1 a2))) +(define check:mov + (λ (a1 a2 n) + (unless (or (register? a1) (offset? a1)) + (error n "expects register or offset; given ~v" a1)) + (unless (or (register? a2) (offset? a2) (exact-integer? a2) (Const? a2)) + (error n "expects register, offset, exact integer, or defined constant; given ~v" a2)) + (when (and (offset? a1) (offset? a2)) + (error n "cannot use two memory locations; given ~v, ~v" a1 a2)) + (when (and (exact-integer? a2) (> (integer-length a2) 64)) + (error n "literal must not exceed 64-bits; given ~v (~v bits)" a2 (integer-length a2))) + (when (and (offset? a1) (exact-integer? a2)) + (error n "cannot use a memory locations and literal; given ~v, ~v; go through a register instead" a1 a2)) + (values a1 a2))) (define check:shift (λ (a1 a2 n) @@ -133,10 +146,10 @@ (instruct Label (x) check:label-symbol) (instruct Call (x) check:target) (instruct Ret () check:none) -(instruct Mov (dst src) (check:src-dest 64)) +(instruct Mov (dst src) check:mov) (instruct Add (dst src) check:arith) (instruct Sub (dst src) check:arith) -(instruct Cmp (a1 a2) (check:src-dest 32)) +(instruct Cmp (a1 a2) check:src-dest) (instruct Jmp (x) check:target) (instruct Je (x) check:target) (instruct Jne (x) check:target) @@ -144,9 +157,9 @@ (instruct Jle (x) check:target) (instruct Jg (x) check:target) (instruct Jge (x) check:target) -(instruct And (dst src) (check:src-dest 32)) -(instruct Or (dst src) (check:src-dest 32)) -(instruct Xor (dst src) (check:src-dest 32)) +(instruct And (dst src) check:src-dest) +(instruct Or (dst src) check:src-dest) +(instruct Xor (dst src) check:src-dest) (instruct Sal (dst i) check:shift) (instruct Sar (dst i) check:shift) (instruct Push (a1) check:push)