diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 4de8cb314..202a65ebb 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -3,6 +3,7 @@ package fmt import "core:runtime" import "core:os" import "core:mem" +import "core:bits" import "core:unicode/utf8" import "core:types" import "core:strconv" @@ -1279,6 +1280,20 @@ fmt_arg :: proc(fi: ^Fmt_Info, arg: any, verb: rune) { case typeid: write_typeid(fi.buf, a); + case i16le: fmt_int(fi, u64(a), true, 16, verb); + case u16le: fmt_int(fi, u64(a), false, 16, verb); + case i32le: fmt_int(fi, u64(a), true, 32, verb); + case u32le: fmt_int(fi, u64(a), false, 32, verb); + case i64le: fmt_int(fi, u64(a), true, 64, verb); + case u64le: fmt_int(fi, u64(a), false, 64, verb); + + case i16be: fmt_int(fi, u64(a), true, 16, verb); + case u16be: fmt_int(fi, u64(a), false, 16, verb); + case i32be: fmt_int(fi, u64(a), true, 32, verb); + case u32be: fmt_int(fi, u64(a), false, 32, verb); + case i64be: fmt_int(fi, u64(a), true, 64, verb); + case u64be: fmt_int(fi, u64(a), false, 64, verb); + case: fmt_value(fi, arg, verb); } diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 42cb68aee..679fee1b0 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -40,9 +40,15 @@ Type_Info_Enum_Value :: union { u8, u16, u32, u64, uint, uintptr, }; +Type_Info_Endianness :: enum u8 { + Platform = 0, + Little = 1, + Big = 2, +} + // Variant Types Type_Info_Named :: struct {name: string, base: ^Type_Info}; -Type_Info_Integer :: struct {signed: bool}; +Type_Info_Integer :: struct {signed: bool, endianness: Type_Info_Endianness}; Type_Info_Rune :: struct {}; Type_Info_Float :: struct {}; Type_Info_Complex :: struct {}; diff --git a/src/build_settings.cpp b/src/build_settings.cpp index e8c872c0a..b30ad7527 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -80,6 +80,8 @@ struct BuildContext { String ODIN_ROOT; // Odin ROOT bool ODIN_DEBUG; // Odin in debug mode + TargetEndianKind endian_kind; + // In bytes i64 word_size; // Size of a pointer, must be >= 4 i64 max_align; // max alignment, must be >= 1 (and typically >= word_size) @@ -539,6 +541,7 @@ void init_build_context(void) { bc->ODIN_OS = target_os_names[metrics.os]; bc->ODIN_ARCH = target_arch_names[metrics.arch]; bc->ODIN_ENDIAN = target_endian_names[target_endians[metrics.arch]]; + bc->endian_kind = target_endians[metrics.arch]; bc->word_size = metrics.word_size; bc->max_align = metrics.max_align; bc->link_flags = str_lit(" "); diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 28271efb8..5f23615d0 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1304,6 +1304,13 @@ bool check_representable_as_constant(CheckerContext *c, ExactValue in_value, Typ case Basic_i32: case Basic_i64: case Basic_int: + + case Basic_i16le: + case Basic_i32le: + case Basic_i64le: + case Basic_i16be: + case Basic_i32be: + case Basic_i64be: { // return imin <= i && i <= imax; int a = big_int_cmp(&imin, &i); @@ -1318,6 +1325,12 @@ bool check_representable_as_constant(CheckerContext *c, ExactValue in_value, Typ case Basic_uint: case Basic_uintptr: + case Basic_u16le: + case Basic_u32le: + case Basic_u64le: + case Basic_u16be: + case Basic_u32be: + case Basic_u64be: { // return 0ull <= i && i <= umax; int b = big_int_cmp(&i, &umax); diff --git a/src/ir.cpp b/src/ir.cpp index 608122afd..49482b7ea 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -299,7 +299,13 @@ gbAllocator ir_allocator(void) { IR_CONV_KIND(sitofp) \ IR_CONV_KIND(ptrtoint) \ IR_CONV_KIND(inttoptr) \ - IR_CONV_KIND(bitcast) + IR_CONV_KIND(bitcast) \ + IR_CONV_KIND(byteswap) +/* + Odin specifc conversion + byteswap - swap bytes for endian change +*/ + enum irInstrKind { irInstr_Invalid, @@ -852,6 +858,7 @@ irDebugInfo *ir_add_debug_info_local(irProcedure *proc, Entity *e, i32 arg_id); irDebugInfo *ir_add_debug_info_file(irModule *module, AstFile *file); irDebugInfo *ir_add_debug_info_proc(irProcedure *proc); +irValue *ir_emit_byte_swap(irProcedure *proc, irValue *value, Type *t); irValue *ir_alloc_value(irValueKind kind) { irValue *v = gb_alloc_item(ir_allocator(), irValue); @@ -1273,6 +1280,9 @@ irValue *ir_const_int(i64 i) { irValue *ir_const_uintptr(u64 i) { return ir_value_constant(t_uintptr, exact_value_i64(i)); } +irValue *ir_const_u8(u32 i) { + return ir_value_constant(t_u8, exact_value_i64(i)); +} irValue *ir_const_i32(i32 i) { return ir_value_constant(t_i32, exact_value_i64(i)); } @@ -3476,6 +3486,13 @@ irValue *ir_emit_unary_arith(irProcedure *proc, TokenKind op, irValue *x, Type * return ir_emit_conv(proc, cmp, type); } + if (op == Token_Sub && is_type_integer(type) && is_type_different_to_arch_endianness(type)) { + Type *platform_type = integer_endian_type_to_platform_type(type); + irValue *v = ir_emit_byte_swap(proc, x, platform_type); + irValue *res = ir_emit(proc, ir_instr_unary_op(proc, op, v, platform_type)); + return ir_emit_byte_swap(proc, res, type); + } + return ir_emit(proc, ir_instr_unary_op(proc, op, x, type)); } @@ -3607,6 +3624,24 @@ irValue *ir_emit_arith(irProcedure *proc, TokenKind op, irValue *left, irValue * } #endif + if (is_type_integer(type) && is_type_different_to_arch_endianness(type)) { + switch (op) { + case Token_AndNot: + case Token_And: + case Token_Or: + case Token_Xor: + goto handle_op; + } + Type *platform_type = integer_endian_type_to_platform_type(type); + irValue *x = ir_emit_byte_swap(proc, left, integer_endian_type_to_platform_type(t_left)); + irValue *y = ir_emit_byte_swap(proc, right, integer_endian_type_to_platform_type(t_right)); + + irValue *res = ir_emit_arith(proc, op, x, y, platform_type); + + return ir_emit_byte_swap(proc, res, type); + } + +handle_op: switch (op) { case Token_Shl: case Token_Shr: @@ -3878,6 +3913,16 @@ irValue *ir_emit_comp(irProcedure *proc, TokenKind op_kind, irValue *left, irVal } } + if (op_kind != Token_CmpEq && op_kind != Token_NotEq) { + Type *t = ir_type(left); + if (is_type_integer(t) && is_type_different_to_arch_endianness(t)) { + Type *platform_type = integer_endian_type_to_platform_type(t); + irValue *x = ir_emit_byte_swap(proc, left, platform_type); + irValue *y = ir_emit_byte_swap(proc, right, platform_type); + return ir_emit(proc, ir_instr_binary_op(proc, op_kind, x, y, t_llvm_bool)); + } + } + return ir_emit(proc, ir_instr_binary_op(proc, op_kind, left, right, t_llvm_bool)); } @@ -4325,6 +4370,12 @@ irValue *ir_emit_uintptr_to_ptr(irProcedure *proc, irValue *value, Type *t) { return ir_emit(proc, ir_instr_conv(proc, irConv_inttoptr, value, vt, t)); } +irValue *ir_emit_byte_swap(irProcedure *proc, irValue *value, Type *t) { + Type *vt = core_type(ir_type(value)); + GB_ASSERT(type_size_of(vt) == type_size_of(t)); + return ir_emit(proc, ir_instr_conv(proc, irConv_byteswap, value, vt, t)); +} + void ir_emit_store_union_variant(irProcedure *proc, irValue *parent, irValue *variant, Type *variant_type) { gbAllocator a = ir_allocator(); @@ -4397,12 +4448,18 @@ irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) { return ir_emit(proc, ir_instr_conv(proc, irConv_zext, value, src_type, t)); } + // integer -> integer if (is_type_integer(src) && is_type_integer(dst)) { GB_ASSERT(src->kind == Type_Basic && dst->kind == Type_Basic); i64 sz = type_size_of(default_type(src)); i64 dz = type_size_of(default_type(dst)); + + if (sz > 1 && is_type_different_to_arch_endianness(src)) { + Type *platform_src_type = integer_endian_type_to_platform_type(src); + value = ir_emit_byte_swap(proc, value, platform_src_type); + } irConvKind kind = irConv_trunc; if (dz < sz) { @@ -4419,7 +4476,13 @@ irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) { } } - return ir_emit(proc, ir_instr_conv(proc, kind, value, src_type, t)); + if (dz > 1 && is_type_different_to_arch_endianness(dst)) { + Type *platform_dst_type = integer_endian_type_to_platform_type(dst); + irValue *res = ir_emit(proc, ir_instr_conv(proc, kind, value, src_type, platform_dst_type)); + return ir_emit_byte_swap(proc, res, t); + } else { + return ir_emit(proc, ir_instr_conv(proc, kind, value, src_type, t)); + } } // boolean -> boolean/integer @@ -4454,40 +4517,6 @@ irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) { gbAllocator a = ir_allocator(); i64 sz = type_size_of(src); i64 dz = type_size_of(dst); - // if (sz == 2) { - // switch (dz) { - // case 2: return value; - // case 4: { - // auto args = array_make(ir_allocator(), 1); - // args[0] = value; - // return ir_emit_runtime_call(proc, "gnu_h2f_ieee", args); - // break; - // } - // case 8: { - // auto args = array_make(ir_allocator(), 1); - // args[0] = value; - // return ir_emit_runtime_call(proc, "f16_to_f64", args); - // break; - // } - // } - // } else if (dz == 2) { - // switch (sz) { - // case 2: return value; - // case 4: { - // auto args = array_make(ir_allocator(), 1); - // args[0] = value; - // return ir_emit_runtime_call(proc, "gnu_f2h_ieee", args); - // break; - // } - // case 8: { - // auto args = array_make(ir_allocator(), 1); - // args[0] = value; - // return ir_emit_runtime_call(proc, "truncdfhf2", args); - // break; - // } - // } - // } - irConvKind kind = irConv_fptrunc; if (dz >= sz) { kind = irConv_fpext; @@ -9128,12 +9157,36 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info case Basic_u32: case Basic_i64: case Basic_u64: + + case Basic_i16le: + case Basic_u16le: + case Basic_i32le: + case Basic_u32le: + case Basic_i64le: + case Basic_u64le: + case Basic_i16be: + case Basic_u16be: + case Basic_i32be: + case Basic_u32be: + case Basic_i64be: + case Basic_u64be: + + case Basic_int: case Basic_uint: case Basic_uintptr: { tag = ir_emit_conv(proc, variant_ptr, t_type_info_integer_ptr); irValue *is_signed = ir_const_bool((t->Basic.flags & BasicFlag_Unsigned) == 0); ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), is_signed); + // NOTE(bill): This is matches the runtime layout + u8 endianness_value = 0; + if (t->Basic.flags & BasicFlag_EndianLittle) { + endianness_value = 1; + } else if (t->Basic.flags & BasicFlag_EndianBig) { + endianness_value = 2; + } + irValue *endianness = ir_const_u8(endianness_value); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 1), endianness); break; } diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 2f3b691bb..4f7935f81 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -69,13 +69,21 @@ void ir_write_u64(irFileBuffer *f, u64 i) { String str = u64_to_string(i, f->buf, IR_FILE_BUFFER_BUF_LEN-1); ir_write_string(f, str); } -void ir_write_big_int(irFileBuffer *f, BigInt const &x) { +void ir_write_big_int(irFileBuffer *f, BigInt const &x, Type *type, bool swap_endian) { i64 i = 0; if (x.neg) { i = big_int_to_i64(&x); } else { i = cast(i64)big_int_to_u64(&x); } + if (swap_endian) { + i64 size = type_size_of(type); + switch (size) { + case 2: i = cast(i64)cast(i16)gb_endian_swap16(cast(u16)cast(i16)i); break; + case 4: i = cast(i64)cast(i32)gb_endian_swap32(cast(u32)cast(i32)i); break; + case 8: i = cast(i64)gb_endian_swap64(cast(u64)i); break; + } + } ir_write_i64(f, i); } @@ -352,6 +360,20 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t, bool in_struct) { case Basic_i64: ir_write_str_lit(f, "i64"); return; case Basic_u64: ir_write_str_lit(f, "i64"); return; + case Basic_i16le: ir_write_str_lit(f, "i16"); return; + case Basic_u16le: ir_write_str_lit(f, "i16"); return; + case Basic_i32le: ir_write_str_lit(f, "i32"); return; + case Basic_u32le: ir_write_str_lit(f, "i32"); return; + case Basic_i64le: ir_write_str_lit(f, "i64"); return; + case Basic_u64le: ir_write_str_lit(f, "i64"); return; + + case Basic_i16be: ir_write_str_lit(f, "i16"); return; + case Basic_u16be: ir_write_str_lit(f, "i16"); return; + case Basic_i32be: ir_write_str_lit(f, "i32"); return; + case Basic_u32be: ir_write_str_lit(f, "i32"); return; + case Basic_i64be: ir_write_str_lit(f, "i64"); return; + case Basic_u64be: ir_write_str_lit(f, "i64"); return; + case Basic_rune: ir_write_str_lit(f, "i32"); return; case Basic_int: @@ -677,13 +699,13 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * ir_write_str_lit(f, "inttoptr ("); ir_print_type(f, m, t_int); ir_write_byte(f, ' '); - ir_write_big_int(f, value.value_integer); + ir_write_big_int(f, value.value_integer, type, is_type_different_to_arch_endianness(type)); ir_write_str_lit(f, " to "); ir_print_type(f, m, t_rawptr); ir_write_str_lit(f, ")"); } } else { - ir_write_big_int(f, value.value_integer); + ir_write_big_int(f, value.value_integer, type, is_type_different_to_arch_endianness(type)); } break; } @@ -1550,15 +1572,25 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) { case irInstr_Conv: { irInstrConv *c = &instr->Conv; - ir_fprintf(f, "%%%d = ", value->index); - ir_write_string(f, ir_conv_strings[c->kind]); - ir_write_byte(f, ' '); - ir_print_type(f, m, c->from); - ir_write_byte(f, ' '); - ir_print_value(f, m, c->value, c->from); - ir_write_str_lit(f, " to "); - ir_print_type(f, m, c->to); - ir_print_debug_location(f, m, value); + if (c->kind == irConv_byteswap) { + int sz = cast(int)(8*type_size_of(c->from)); + ir_fprintf(f, "%%%d = call i%d @llvm.bswap.i%d(", value->index, sz, sz); + ir_print_type(f, m, c->from); + ir_write_byte(f, ' '); + ir_print_value(f, m, c->value, c->from); + ir_write_byte(f, ')'); + ir_print_debug_location(f, m, value); + } else { + ir_fprintf(f, "%%%d = ", value->index); + ir_write_string(f, ir_conv_strings[c->kind]); + ir_write_byte(f, ' '); + ir_print_type(f, m, c->from); + ir_write_byte(f, ' '); + ir_print_value(f, m, c->value, c->from); + ir_write_str_lit(f, " to "); + ir_print_type(f, m, c->to); + ir_print_debug_location(f, m, value); + } break; } @@ -2096,6 +2128,9 @@ void print_llvm_ir(irGen *ir) { ir_write_str_lit(f, "} ; Basic_any\n"); ir_write_str_lit(f, "declare void @llvm.dbg.declare(metadata, metadata, metadata) #3 \n"); + ir_write_str_lit(f, "declare i16 @llvm.bswap.i16(i16) #3 \n"); + ir_write_str_lit(f, "declare i32 @llvm.bswap.i32(i32) #3 \n"); + ir_write_str_lit(f, "declare i64 @llvm.bswap.i64(i64) #3 \n"); ir_write_byte(f, '\n'); diff --git a/src/types.cpp b/src/types.cpp index 6024ecfd1..635cfc207 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -40,6 +40,22 @@ enum BasicKind { Basic_typeid, + // Endian Specific Types + Basic_i16le, + Basic_u16le, + Basic_i32le, + Basic_u32le, + Basic_i64le, + Basic_u64le, + + Basic_i16be, + Basic_u16be, + Basic_i32be, + Basic_u32be, + Basic_i64be, + Basic_u64be, + + // Untyped types Basic_UntypedBool, Basic_UntypedInteger, Basic_UntypedFloat, @@ -67,6 +83,9 @@ enum BasicFlag { BasicFlag_LLVM = GB_BIT(10), + BasicFlag_EndianLittle = GB_BIT(13), + BasicFlag_EndianBig = GB_BIT(14), + BasicFlag_Numeric = BasicFlag_Integer | BasicFlag_Float | BasicFlag_Complex, BasicFlag_Ordered = BasicFlag_Integer | BasicFlag_Float | BasicFlag_String | BasicFlag_Pointer | BasicFlag_Rune, BasicFlag_OrderedNumeric = BasicFlag_Integer | BasicFlag_Float | BasicFlag_Rune, @@ -313,6 +332,22 @@ gb_global Type basic_types[] = { {Type_Basic, {Basic_typeid, 0, -1, STR_LIT("typeid")}}, + // Endian + {Type_Basic, {Basic_i16le, BasicFlag_Integer | BasicFlag_EndianBig, 2, STR_LIT("i16le")}}, + {Type_Basic, {Basic_u16le, BasicFlag_Integer | BasicFlag_Unsigned | BasicFlag_EndianBig, 2, STR_LIT("u16le")}}, + {Type_Basic, {Basic_i32le, BasicFlag_Integer | BasicFlag_EndianBig, 4, STR_LIT("i32le")}}, + {Type_Basic, {Basic_u32le, BasicFlag_Integer | BasicFlag_Unsigned | BasicFlag_EndianBig, 4, STR_LIT("u32le")}}, + {Type_Basic, {Basic_i64le, BasicFlag_Integer | BasicFlag_EndianBig, 8, STR_LIT("i64le")}}, + {Type_Basic, {Basic_u64le, BasicFlag_Integer | BasicFlag_Unsigned | BasicFlag_EndianBig, 8, STR_LIT("u64le")}}, + + {Type_Basic, {Basic_i16be, BasicFlag_Integer | BasicFlag_EndianBig, 2, STR_LIT("i16be")}}, + {Type_Basic, {Basic_u16be, BasicFlag_Integer | BasicFlag_Unsigned | BasicFlag_EndianBig, 2, STR_LIT("u16be")}}, + {Type_Basic, {Basic_i32be, BasicFlag_Integer | BasicFlag_EndianBig, 4, STR_LIT("i32be")}}, + {Type_Basic, {Basic_u32be, BasicFlag_Integer | BasicFlag_Unsigned | BasicFlag_EndianBig, 4, STR_LIT("u32be")}}, + {Type_Basic, {Basic_i64be, BasicFlag_Integer | BasicFlag_EndianBig, 8, STR_LIT("i64be")}}, + {Type_Basic, {Basic_u64be, BasicFlag_Integer | BasicFlag_Unsigned | BasicFlag_EndianBig, 8, STR_LIT("u64be")}}, + + // Untyped types {Type_Basic, {Basic_UntypedBool, BasicFlag_Boolean | BasicFlag_Untyped, 0, STR_LIT("untyped bool")}}, {Type_Basic, {Basic_UntypedInteger, BasicFlag_Integer | BasicFlag_Untyped, 0, STR_LIT("untyped integer")}}, {Type_Basic, {Basic_UntypedFloat, BasicFlag_Float | BasicFlag_Untyped, 0, STR_LIT("untyped float")}}, @@ -978,6 +1013,75 @@ bool is_type_map(Type *t) { } +bool is_type_integer_endian_big(Type *t) { + t = core_type(t); + if (t->kind == Type_Basic) { + if (t->Basic.flags & BasicFlag_EndianBig) { + return true; + } else if (t->Basic.flags & BasicFlag_EndianLittle) { + return false; + } + } else if (t->kind == Type_BitSet) { + return is_type_integer_endian_big(t->BitSet.elem); + } else { + GB_PANIC("Unsupported type: %s", type_to_string); + } + return build_context.endian_kind == TargetEndian_Big; +} + +bool is_type_integer_endian_little(Type *t) { + t = core_type(t); + if (t->kind == Type_Basic) { + if (t->Basic.flags & BasicFlag_EndianLittle) { + return true; + } else if (t->Basic.flags & BasicFlag_EndianBig) { + return false; + } + } else if (t->kind == Type_BitSet) { + return is_type_integer_endian_little(t->BitSet.elem); + } else { + GB_PANIC("Unsupported type: %s", type_to_string); + } + return build_context.endian_kind == TargetEndian_Little; +} + +bool is_type_different_to_arch_endianness(Type *t) { + switch (build_context.endian_kind) { + case TargetEndian_Little: + return !is_type_integer_endian_little(t); + case TargetEndian_Big: + return !is_type_integer_endian_big(t); + } + return false; +} + +Type *integer_endian_type_to_platform_type(Type *t) { + t = core_type(t); + if (t->kind == Type_BitSet) { + t = core_type(t->BitSet.elem); + } + GB_ASSERT(t->kind == Type_Basic); + + switch (t->Basic.kind) { + // Endian Specific Types + case Basic_i16le: return t_i16; + case Basic_u16le: return t_u16; + case Basic_i32le: return t_i32; + case Basic_u32le: return t_u32; + case Basic_i64le: return t_i64; + case Basic_u64le: return t_u64; + + case Basic_i16be: return t_i16; + case Basic_u16be: return t_u16; + case Basic_i32be: return t_i32; + case Basic_u32be: return t_u32; + case Basic_i64be: return t_i64; + case Basic_u64be: return t_u64; + } + + return t; +} + bool is_type_any(Type *t) {