From ebff16454c438ee19cbaf14c58424a05e95143e1 Mon Sep 17 00:00:00 2001 From: Dry-Eggo Date: Sun, 12 Oct 2025 12:25:12 -0700 Subject: [PATCH 1/3] Added Logical OR (||) and Logical AND (&&) binary operators for gas_x86_64 --- examples/lop.b | 11 ++++++++ src/b.rs | 9 ++++++ src/codegen/gas_aarch64/mod.rs | 4 ++- src/codegen/gas_x86_64/mod.rs | 51 ++++++++++++++++++++++++++++++++-- src/codegen/mos6502.rs | 2 ++ src/codegen/uxn/mod.rs | 6 ++-- src/ir.rs | 4 +++ src/lexer.rs | 8 +++++- 8 files changed, 88 insertions(+), 7 deletions(-) create mode 100644 examples/lop.b diff --git a/examples/lop.b b/examples/lop.b new file mode 100644 index 00000000..b095ef4c --- /dev/null +++ b/examples/lop.b @@ -0,0 +1,11 @@ +main () { + extrn printf; + printf("LOP1: (%d)\n", 1 || 0); + printf("LOP2: (%d)\n", 1 || 1); + printf("LOP3: (%d)\n", 0 || 1); + printf("LOP4: (%d)\n", 0 || 0); + printf("LOP5: (%d)\n", 0 && 1); + printf("LOP6: (%d)\n", 1 && 0); + printf("LOP7: (%d)\n", 1 && 1); + printf("LOP8: (%d)\n", 0 && 0); +} \ No newline at end of file diff --git a/src/b.rs b/src/b.rs index 0193e3da..406d025a 100644 --- a/src/b.rs +++ b/src/b.rs @@ -237,6 +237,8 @@ pub unsafe fn define_goto_label(c: *mut Compiler, name: *const c_char, loc: Loc, // The higher the index of the row in this table the higher the precedence of the Binop pub const PRECEDENCE: *const [*const [Binop]] = &[ + &[Binop::LogOr], + &[Binop::LogAnd], &[Binop::BitOr], &[Binop::BitAnd], &[Binop::BitShl, Binop::BitShr], @@ -283,6 +285,8 @@ impl Binop { Token::And => Some(Binop::BitAnd), Token::Shl => Some(Binop::BitShl), Token::Shr => Some(Binop::BitShr), + Token::Lor => Some(Binop::LogOr), + Token::Land => Some(Binop::LogAnd), _ => None, } } @@ -445,6 +449,11 @@ pub unsafe fn compile_primary_expression(l: *mut Lexer, c: *mut Compiler) -> Opt lexer::get_token(l)?; (arg, is_lvalue) = match (*l).token { + // Token::Lor => { + // let (rhs, _ ) = compile_expression(l, c)?; + // compile_binop(arg, rhs, Binop::LogOr, (*l).loc, c); + // Some((arg, false)) + // } Token::OParen => Some((compile_function_call(l, c, arg)?, false)), Token::OBracket => { let (offset, _) = compile_expression(l, c)?; diff --git a/src/codegen/gas_aarch64/mod.rs b/src/codegen/gas_aarch64/mod.rs index 99b8020b..bc2371f4 100644 --- a/src/codegen/gas_aarch64/mod.rs +++ b/src/codegen/gas_aarch64/mod.rs @@ -188,8 +188,10 @@ pub unsafe fn generate_function(name: *const c_char, _name_loc: Loc, params_coun }, Op::Binop {binop, index, lhs, rhs} => { match binop { + Binop::LogAnd => todo!(), + Binop::LogOr => todo!(), Binop::BitOr => { - load_arg_to_reg(lhs, c!("x0"), output, op.loc, os); + load_arg_to_reg(lhs, c!("x0"), output, op.loc, os); load_arg_to_reg(rhs, c!("x1"), output, op.loc, os); sb_appendf(output, c!(" orr x0, x0, x1\n")); sb_appendf(output, c!(" str x0, [x29, -%zu]\n"), index*8); diff --git a/src/codegen/gas_x86_64/mod.rs b/src/codegen/gas_x86_64/mod.rs index 4d918594..09045147 100644 --- a/src/codegen/gas_x86_64/mod.rs +++ b/src/codegen/gas_x86_64/mod.rs @@ -179,9 +179,54 @@ pub unsafe fn generate_function(name: *const c_char, name_loc: Loc, func_index: } Op::Binop {binop, index, lhs, rhs} => { load_arg_to_reg(lhs, c!("rax"), output, os); - load_arg_to_reg(rhs, c!("rcx"), output, os); + + if let Binop::LogOr | Binop::LogAnd = binop { + // Short-Circuit Logical Or/And: defer loading rhs to rcx + // only evaluate rhs if lhs == 0 (OR) or lhs == 1 (AND) + } + else { + load_arg_to_reg(rhs, c!("rcx"), output, os); + } + + static mut BRANCH_COUNTER: usize = 0; match binop { - Binop::BitOr => { sb_appendf(output, c!(" orq %%rcx, %%rax\n")); } + Binop::LogOr => { + let branch_count = BRANCH_COUNTER; + BRANCH_COUNTER += 1; + sb_appendf(output, c!(" cmp $0, %%rax\n")); + sb_appendf(output, c!(" jne .L%dtrue\n"), branch_count); + + load_arg_to_reg(rhs, c!("rcx"), output, os); + sb_appendf(output, c!(" cmp $0, %%rcx\n")); + sb_appendf(output, c!(" jne .L%dtrue\n"), branch_count); + + sb_appendf(output, c!(" movq $0, %%rax\n")); + sb_appendf(output, c!(" jmp .L%dend\n"), branch_count); + + sb_appendf(output, c!(".L%dtrue:\n"), branch_count); + sb_appendf(output, c!(" movq $1,%%rax\n")); + sb_appendf(output, c!(".L%dend:\n"), branch_count); + } + Binop::LogAnd => { + let branch_count = BRANCH_COUNTER; + BRANCH_COUNTER += 1; + sb_appendf(output, c!(" cmp $0, %%rax\n")); + sb_appendf(output, c!(" je .L%dtrue\n"), branch_count); + + load_arg_to_reg(rhs, c!("rcx"), output, os); + sb_appendf(output, c!(" cmp $0, %%rcx\n")); + sb_appendf(output, c!(" je .L%dtrue\n"), branch_count); + + sb_appendf(output, c!(" movq $1, %%rax\n")); + sb_appendf(output, c!(" jmp .L%dend\n"), branch_count); + + sb_appendf(output, c!(".L%dtrue:\n"), branch_count); + sb_appendf(output, c!(" movq $0,%%rax\n")); + sb_appendf(output, c!(".L%dend:\n"), branch_count); + } + Binop::BitOr => { + sb_appendf(output, c!(" orq %%rcx, %%rax\n")); + } Binop::BitAnd => { sb_appendf(output, c!(" andq %%rcx, %%rax\n")); } Binop::BitShl => { load_arg_to_reg(rhs, c!("rcx"), output, os); @@ -216,7 +261,7 @@ pub unsafe fn generate_function(name: *const c_char, name_loc: Loc, func_index: Binop::NotEqual => sb_appendf(output, c!(" setne %%dl\n")), Binop::GreaterEqual => sb_appendf(output, c!(" setge %%dl\n")), Binop::LessEqual => sb_appendf(output, c!(" setle %%dl\n")), - _ => unreachable!(), + _ => unreachable!(), }; sb_appendf(output, c!(" movq %%rdx, -%zu(%%rbp)\n"), index * 8); continue; diff --git a/src/codegen/mos6502.rs b/src/codegen/mos6502.rs index 57114c8b..00012a91 100644 --- a/src/codegen/mos6502.rs +++ b/src/codegen/mos6502.rs @@ -943,6 +943,8 @@ pub unsafe fn generate_function(name: *const c_char, loc: Loc, params_count: usi }, Op::Binop {binop, index, lhs, rhs} => { match binop { + Binop::LogAnd => todo!("Logical operator `&&` not implemented on this target"), + Binop::LogOr => todo!("Logical operator `||` not implemented on this target"), Binop::BitOr => { load_two_args(out, lhs, rhs, op, asm); diff --git a/src/codegen/uxn/mod.rs b/src/codegen/uxn/mod.rs index 48995ad9..cb3709d3 100644 --- a/src/codegen/uxn/mod.rs +++ b/src/codegen/uxn/mod.rs @@ -335,7 +335,7 @@ pub unsafe fn generate_function(name: *const c_char, name_loc: Loc, params_count for i in 0..body.len() { let op = (*body)[i]; match op.opcode { - Op::Bogus => unreachable!("bogus-amogus"), + Op::Bogus => unreachable!("bogus-amogus"), Op::UnaryNot {result, arg} => { load_arg(arg, op.loc, output, assembler); // if arg == 0 then 1 else 0 @@ -572,7 +572,9 @@ pub unsafe fn generate_function(name: *const c_char, name_loc: Loc, params_count write_op(output, UxnOp::SFT2); store_auto(output, index); } - Op::AutoAssign {index, arg} => { + Op::Binop {binop: Binop::LogAnd, .. } => todo!("Logical operator `&&` not implemented on this target"), + Op::Binop {binop: Binop::LogOr, .. } => todo!("Logical operator `||` not implemented on this target"), + Op::AutoAssign {index, arg} => { load_arg(arg, op.loc, output, assembler); store_auto(output, index); } diff --git a/src/ir.rs b/src/ir.rs index 75a4f13b..08d7fa6e 100644 --- a/src/ir.rs +++ b/src/ir.rs @@ -50,6 +50,8 @@ pub enum Binop { BitAnd, BitShl, BitShr, + LogOr, + LogAnd, } #[derive(Clone, Copy)] @@ -200,6 +202,8 @@ pub unsafe fn dump_op(op: OpWithLocation, output: *mut String_Builder) { sb_appendf(output, c!(" auto[%zu] = "), index); dump_arg(output, lhs); match binop { + Binop::LogAnd => sb_appendf(output, c!(" && ")), + Binop::LogOr => sb_appendf(output, c!(" || ")), Binop::BitOr => sb_appendf(output, c!(" | ")), Binop::BitAnd => sb_appendf(output, c!(" & ")), Binop::BitShl => sb_appendf(output, c!(" << ")), diff --git a/src/lexer.rs b/src/lexer.rs index 926d6472..d3480060 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -80,7 +80,9 @@ pub enum Token { Colon, SemiColon, Comma, - + Lor, // || + Land, // && + // Keywords Auto, Extrn, @@ -146,6 +148,8 @@ pub unsafe fn display_token(token: Token) -> *const c_char { Token::Colon => c!("`:`"), Token::SemiColon => c!("`;`"), Token::Comma => c!("`,`"), + Token::Lor => c!("`||`"), + Token::Land => c!("`&&`"), Token::Auto => c!("keyword `auto`"), Token::Extrn => c!("keyword `extrn`"), @@ -193,8 +197,10 @@ pub const PUNCTS: *const [(*const c_char, Token)] = &[ (c!("/="), Token::DivEq), (c!("/"), Token::Div), (c!("|="), Token::OrEq), + (c!("||"), Token::Lor), (c!("|"), Token::Or), (c!("&="), Token::AndEq), + (c!("&&"), Token::Land), (c!("&"), Token::And), (c!("=="), Token::EqEq), (c!("="), Token::Eq), From 6af79e986f93ab09da57ed2f5e8003fc33733955 Mon Sep 17 00:00:00 2001 From: Dry-Eggo Date: Sun, 12 Oct 2025 12:31:07 -0700 Subject: [PATCH 2/3] Clean up comments --- src/b.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/b.rs b/src/b.rs index 406d025a..827dc7cd 100644 --- a/src/b.rs +++ b/src/b.rs @@ -449,11 +449,6 @@ pub unsafe fn compile_primary_expression(l: *mut Lexer, c: *mut Compiler) -> Opt lexer::get_token(l)?; (arg, is_lvalue) = match (*l).token { - // Token::Lor => { - // let (rhs, _ ) = compile_expression(l, c)?; - // compile_binop(arg, rhs, Binop::LogOr, (*l).loc, c); - // Some((arg, false)) - // } Token::OParen => Some((compile_function_call(l, c, arg)?, false)), Token::OBracket => { let (offset, _) = compile_expression(l, c)?; From 5a92e7ca683b465d9ef6d688d796c516e19f261c Mon Sep 17 00:00:00 2001 From: Dry-Eggo Date: Sun, 12 Oct 2025 13:30:27 -0700 Subject: [PATCH 3/3] Move Binop::LogAnd (&&) and Binop::LogOr (||) implementation to the intermediate representation, so they now work for all targets --- examples/lop.b | 2 +- src/b.rs | 66 +++++++++++++++++++++++++++++------ src/codegen/gas_x86_64/mod.rs | 44 +---------------------- src/codegen/mos6502.rs | 3 +- src/codegen/uxn/mod.rs | 3 +- 5 files changed, 60 insertions(+), 58 deletions(-) diff --git a/examples/lop.b b/examples/lop.b index b095ef4c..a33a8e93 100644 --- a/examples/lop.b +++ b/examples/lop.b @@ -4,7 +4,7 @@ main () { printf("LOP2: (%d)\n", 1 || 1); printf("LOP3: (%d)\n", 0 || 1); printf("LOP4: (%d)\n", 0 || 0); - printf("LOP5: (%d)\n", 0 && 1); + printf("LOP5: (%d)\n", 0 && 1); printf("LOP6: (%d)\n", 1 && 0); printf("LOP7: (%d)\n", 1 && 1); printf("LOP8: (%d)\n", 0 && 0); diff --git a/src/b.rs b/src/b.rs index 827dc7cd..1f1a9549 100644 --- a/src/b.rs +++ b/src/b.rs @@ -532,15 +532,61 @@ pub unsafe fn compile_binop_expression(l: *mut Lexer, c: *mut Compiler, preceden if binop.precedence() != precedence { break; } let (rhs, _) = compile_binop_expression(l, c, precedence + 1)?; - - let index = allocate_auto_var(&mut (*c).auto_vars_ator); - push_opcode(Op::Binop {binop, index, lhs, rhs}, (*l).loc, c); - lhs = Arg::AutoVar(index); - - lvalue = false; - - saved_point = (*l).parse_point; - lexer::get_token(l)?; + match binop { + Binop::LogOr => { + let result = allocate_auto_var(&mut (*c).auto_vars_ator); + + let t_label = allocate_label_index(c); + let e_label = allocate_label_index(c); + + push_opcode(Op::Binop { binop: Binop::Equal, index: result, lhs, rhs: Arg::Literal(0) }, (*l).loc, c); + push_opcode(Op::JmpIfNotLabel {label: t_label, arg: Arg::AutoVar(result) }, (*l).loc, c); + push_opcode(Op::Binop { binop: Binop::Equal, index: result, lhs: rhs, rhs: Arg::Literal(0) }, (*l).loc, c); + push_opcode(Op::JmpIfNotLabel {label: t_label, arg: Arg::AutoVar(result) }, (*l).loc, c); + push_opcode(Op::AutoAssign {index: result, arg: Arg::Literal(0)}, (*l).loc, c); + push_opcode(Op::JmpLabel {label: e_label}, (*l).loc, c); + + push_opcode(Op::Label {label: t_label}, (*l).loc, c); + push_opcode(Op::AutoAssign {index: result, arg: Arg::Literal(1)}, (*l).loc, c); + push_opcode(Op::Label {label: e_label}, (*l).loc, c); + + lhs = Arg::AutoVar(result); + lvalue = false; + saved_point = (*l).parse_point; + lexer::get_token(l)?; + } + Binop::LogAnd => { + let result = allocate_auto_var(&mut (*c).auto_vars_ator); + + let t_label = allocate_label_index(c); + let e_label = allocate_label_index(c); + + push_opcode(Op::Binop { binop: Binop::Equal, index: result, lhs, rhs: Arg::Literal(1) }, (*l).loc, c); + push_opcode(Op::JmpIfNotLabel {label: t_label, arg: Arg::AutoVar(result) }, (*l).loc, c); + push_opcode(Op::Binop { binop: Binop::Equal, index: result, lhs: rhs, rhs: Arg::Literal(1) }, (*l).loc, c); + push_opcode(Op::JmpIfNotLabel {label: t_label, arg: Arg::AutoVar(result) }, (*l).loc, c); + push_opcode(Op::AutoAssign {index: result, arg: Arg::Literal(1)}, (*l).loc, c); + push_opcode(Op::JmpLabel {label: e_label}, (*l).loc, c); + + push_opcode(Op::Label {label: t_label}, (*l).loc, c); + push_opcode(Op::AutoAssign {index: result, arg: Arg::Literal(0)}, (*l).loc, c); + push_opcode(Op::Label {label: e_label}, (*l).loc, c); + + lhs = Arg::AutoVar(result); + lvalue = false; + saved_point = (*l).parse_point; + lexer::get_token(l)?; + } + _ => { + let index = allocate_auto_var(&mut (*c).auto_vars_ator); + push_opcode(Op::Binop {binop, index, lhs, rhs}, (*l).loc, c); + lhs = Arg::AutoVar(index); + + lvalue = false; + saved_point = (*l).parse_point; + lexer::get_token(l)?; + } + } } } } @@ -565,7 +611,7 @@ pub unsafe fn compile_assign_expression(l: *mut Lexer, c: *mut Compiler) -> Opti } if let Some(binop) = binop { - compile_binop(lhs, rhs, binop, binop_loc, c); + compile_binop(lhs, rhs, binop, binop_loc, c); } else { match lhs { Arg::Deref(index) => { diff --git a/src/codegen/gas_x86_64/mod.rs b/src/codegen/gas_x86_64/mod.rs index 09045147..b978e79f 100644 --- a/src/codegen/gas_x86_64/mod.rs +++ b/src/codegen/gas_x86_64/mod.rs @@ -179,51 +179,9 @@ pub unsafe fn generate_function(name: *const c_char, name_loc: Loc, func_index: } Op::Binop {binop, index, lhs, rhs} => { load_arg_to_reg(lhs, c!("rax"), output, os); - - if let Binop::LogOr | Binop::LogAnd = binop { - // Short-Circuit Logical Or/And: defer loading rhs to rcx - // only evaluate rhs if lhs == 0 (OR) or lhs == 1 (AND) - } - else { - load_arg_to_reg(rhs, c!("rcx"), output, os); - } + load_arg_to_reg(rhs, c!("rcx"), output, os); - static mut BRANCH_COUNTER: usize = 0; match binop { - Binop::LogOr => { - let branch_count = BRANCH_COUNTER; - BRANCH_COUNTER += 1; - sb_appendf(output, c!(" cmp $0, %%rax\n")); - sb_appendf(output, c!(" jne .L%dtrue\n"), branch_count); - - load_arg_to_reg(rhs, c!("rcx"), output, os); - sb_appendf(output, c!(" cmp $0, %%rcx\n")); - sb_appendf(output, c!(" jne .L%dtrue\n"), branch_count); - - sb_appendf(output, c!(" movq $0, %%rax\n")); - sb_appendf(output, c!(" jmp .L%dend\n"), branch_count); - - sb_appendf(output, c!(".L%dtrue:\n"), branch_count); - sb_appendf(output, c!(" movq $1,%%rax\n")); - sb_appendf(output, c!(".L%dend:\n"), branch_count); - } - Binop::LogAnd => { - let branch_count = BRANCH_COUNTER; - BRANCH_COUNTER += 1; - sb_appendf(output, c!(" cmp $0, %%rax\n")); - sb_appendf(output, c!(" je .L%dtrue\n"), branch_count); - - load_arg_to_reg(rhs, c!("rcx"), output, os); - sb_appendf(output, c!(" cmp $0, %%rcx\n")); - sb_appendf(output, c!(" je .L%dtrue\n"), branch_count); - - sb_appendf(output, c!(" movq $1, %%rax\n")); - sb_appendf(output, c!(" jmp .L%dend\n"), branch_count); - - sb_appendf(output, c!(".L%dtrue:\n"), branch_count); - sb_appendf(output, c!(" movq $0,%%rax\n")); - sb_appendf(output, c!(".L%dend:\n"), branch_count); - } Binop::BitOr => { sb_appendf(output, c!(" orq %%rcx, %%rax\n")); } diff --git a/src/codegen/mos6502.rs b/src/codegen/mos6502.rs index 00012a91..d53a50b8 100644 --- a/src/codegen/mos6502.rs +++ b/src/codegen/mos6502.rs @@ -943,8 +943,6 @@ pub unsafe fn generate_function(name: *const c_char, loc: Loc, params_count: usi }, Op::Binop {binop, index, lhs, rhs} => { match binop { - Binop::LogAnd => todo!("Logical operator `&&` not implemented on this target"), - Binop::LogOr => todo!("Logical operator `||` not implemented on this target"), Binop::BitOr => { load_two_args(out, lhs, rhs, op, asm); @@ -1272,6 +1270,7 @@ pub unsafe fn generate_function(name: *const c_char, loc: Loc, params_count: usi // zero extend result instr8(out, LDY, IMM, 0); }, + _ => unreachable!(), } store_auto(out, index, asm); }, diff --git a/src/codegen/uxn/mod.rs b/src/codegen/uxn/mod.rs index cb3709d3..e24ff394 100644 --- a/src/codegen/uxn/mod.rs +++ b/src/codegen/uxn/mod.rs @@ -572,8 +572,6 @@ pub unsafe fn generate_function(name: *const c_char, name_loc: Loc, params_count write_op(output, UxnOp::SFT2); store_auto(output, index); } - Op::Binop {binop: Binop::LogAnd, .. } => todo!("Logical operator `&&` not implemented on this target"), - Op::Binop {binop: Binop::LogOr, .. } => todo!("Logical operator `||` not implemented on this target"), Op::AutoAssign {index, arg} => { load_arg(arg, op.loc, output, assembler); store_auto(output, index); @@ -655,6 +653,7 @@ pub unsafe fn generate_function(name: *const c_char, name_loc: Loc, params_count write_op(output, UxnOp::ADD2); store_auto(output, result); }, + _ => unreachable!() } }