コンパイラ作成(73) char型の加減算
今回の目標
char型のサポートを進めるよ。
// char型 int main() { char a = 10, b = 8, c; c = a + b; printf("%hhu + %hhu = %hhu\n", a, b, c); c = a - b; printf("%hhu - %hhu = %hhu\n", a, b, c); }
今回は加減算。
codegen_els
# 式のコード生成(二項演算の右側被演算子) def codegen_els(op, operand, type_l) if op.str == "+" then ostr = "add " elsif op.str == "-" then ostr = "sub " elsif op.str == "*" then ostr = "imul" elsif op.str == "/" then ostr = "idiv" elsif op.str == "%" then ostr = "idiv" elsif op.str == "==" then ostr = "cmp " elsif op.str == "!=" then ostr = "cmp " elsif op.str == "<" || op.str == "<" || op.str == ">" || op.str == "<=" || op.str == ">=" then ostr = "cmp " else perror "unknown operator \"" + op.str + "\"" end # 右被演算子を評価 type_r = "int" if operand.kind_of?(Array) then if operand[0].size == 2 && operand[0].kind == TK::ID && operand[1].str == "()" then codegen " sub rsp, 8" codegen " push rax" type_r = codegen_func operand codegen " mov r10d, eax" codegen " pop rax" codegen " add rsp, 8" else codegen " sub rsp, 8" codegen " push rax" type_r = codegen_el operand codegen " mov r10d, eax" codegen " pop rax" codegen " add rsp, 8" end str = "r10d" elsif operand.kind == TK::ID then v = get_var operand.str if v == nil then perror "undeclared variable \"" + operand.str + "\"" end type_r = v[0] if is_pointer_type? type_r then str = "qword ptr [rbp - " + v[1].to_s + "]" elsif type_r == "char" then str = "byte ptr [rbp - " + v[1].to_s + "]" else str = "dword ptr [rbp - " + v[1].to_s + "]" end elsif operand.kind == TK::NUMBER then str = operand.str elsif operand.kind == TK::STRING then type_r = "char*" label = addliteral operand.str codegen " lea r10, "+label str = "r10" else perror end # 型チェック if type_l != type_r then if is_pointer_type?(type_l) && type_r == "int" then if op.str != "+" && op.str != "-" then perror "mismatched types to binary operation" end elsif type_l == "int" && is_pointer_type?(type_r) then if op.str != "+" && op.str != "-" then perror "mismatched types to binary operation" end else perror "mismatched types to binary operation" end elsif is_pointer_type? type_l then perror "mismatched types to binary operation" end reg = "eax" if type_l == "char" then reg = "al" end # 左被演算子と右被演算子とで計算 if op.str == "==" then codegen " " + ostr + " eax, " + str codegen " sete al" codegen " and eax, 1" elsif op.str == "!=" then codegen " " + ostr + " eax, " + str codegen " setne al" codegen " and eax, 1" elsif op.str == "<" then codegen " " + ostr + " eax, " + str codegen " setl al" codegen " and eax, 1" elsif op.str == ">" then codegen " " + ostr + " eax, " + str codegen " setg al" codegen " and eax, 1" elsif op.str == "<=" then codegen " " + ostr + " eax, " + str codegen " setle al" codegen " and eax, 1" elsif op.str == ">=" then codegen " " + ostr + " eax, " + str codegen " setge al" codegen " and eax, 1" elsif op.str == "*" || op.str == "/" || op.str == "%" then if str != "r10d" then codegen " mov r10d, " + str end codegen " mov r11, rdx" if op.str == "/" || op.str == "%" then codegen " cdq" end codegen " " + ostr + " r10d" if op.str == "%" then codegen " mov eax, edx" end codegen " mov rdx, r11" else if is_pointer_type?(type_l) && type_r == "int" then size = sizeof type_l[0,type_l.length-1] if str == op.str then codegen " " + ostr + " rax, " + (str.to_i*size).to_s elsif str == "r10d" then codegen " movsx r10, r10d" if size == 4 then codegen " shl r10, 2" codegen " " + ostr + " rax, r10" elsif size == 8 then codegen " shl r10, 2" codegen " " + ostr + " rax, r10" else (0...size).each do codegen " " + ostr + " rax, r10" end end else codegen " mov r10d, " + str codegen " movsx r10, r10d" if size == 4 then codegen " shl r10, 2" codegen " " + ostr + " rax, r10" elsif size == 8 then codegen " shl r10, 3" codegen " " + ostr + " rax, r10" else (0...size).each do codegen " " + ostr + " rax, r10" end end end elsif type_l == "int" && is_pointer_type?(type_r) then size = sizeof type_r[0,type_r.length-1] codegen " movsx rax, eax" if size == 1 then elsif size == 4 then codegen " shl rax, 2" elsif size == 8 then codegen " shl rax, 3" else codegen " mov r11, rax" (0...size-1).each do codegen " add rax, r11" end end codegen " " + ostr + " rax, " + str type_l = type_r else codegen " " + ostr + " #{reg}, " + str end end return type_l end
char型だったら8bitで処理する。
動作テスト
~/myc$ myc p3.myc ~/myc$ ./p3 10 + 8 = 18 10 - 8 = 2 ~/myc$
できたかな。アセンブリコードも見てみるよ。
.intel_syntax noprefix .global main main: push rbp mov rbp, rsp sub rsp, 16 mov eax, 10 mov byte ptr [rbp - 1], al mov eax, 8 mov byte ptr [rbp - 2], al mov al, byte ptr [rbp - 1] add al, byte ptr [rbp - 2] mov byte ptr [rbp - 3], al lea rax, .L.str mov rdi, rax mov al, byte ptr [rbp - 1] mov esi, eax mov al, byte ptr [rbp - 2] mov edx, eax mov al, byte ptr [rbp - 3] mov ecx, eax mov al, 0 call printf mov al, byte ptr [rbp - 1] sub al, byte ptr [rbp - 2] mov byte ptr [rbp - 3], al lea rax, .L.str.1 mov rdi, rax mov al, byte ptr [rbp - 1] mov esi, eax mov al, byte ptr [rbp - 2] mov edx, eax mov al, byte ptr [rbp - 3] mov ecx, eax mov al, 0 call printf .RET_main: add rsp, 16 pop rbp ret .L.str: .asciz "%hhu + %hhu = %hhu\n" .L.str.1: .asciz "%hhu - %hhu = %hhu\n"
8bitの計算になってるから大丈夫そうだね。今日はあんまり時間取れなかったんでちょっとしか進めなかったよ。char型の計算がーっとやっちゃいたかったんだけどね。