Browse Source

Support endian specific float on -llvm-api; fix unary `-` for endian floats

gingerBill 5 years ago
parent
commit
5157619eb7
2 changed files with 125 additions and 17 deletions
  1. 10 4
      src/ir.cpp
  2. 115 13
      src/llvm_backend.cpp

+ 10 - 4
src/ir.cpp

@@ -4124,6 +4124,13 @@ irValue *ir_emit_unary_arith(irProcedure *proc, TokenKind op, irValue *x, Type *
 		return ir_emit_byte_swap(proc, res, type);
 	}
 
+	if (op == Token_Sub && is_type_float(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));
 }
 
@@ -4756,11 +4763,10 @@ irValue *ir_emit_comp(irProcedure *proc, TokenKind op_kind, irValue *left, irVal
 			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));
-		}
-		if (is_type_float(t) && is_type_different_to_arch_endianness(t)) {
+		} else if (is_type_float(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);
+			irValue *x = ir_emit_conv(proc, left, platform_type);
+			irValue *y = ir_emit_conv(proc, right, platform_type);
 			return ir_emit(proc, ir_instr_binary_op(proc, op_kind, x, y, t_llvm_bool));
 		}
 	}

+ 115 - 13
src/llvm_backend.cpp

@@ -681,6 +681,12 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
 		case Basic_f32: return LLVMFloatTypeInContext(ctx);
 		case Basic_f64: return LLVMDoubleTypeInContext(ctx);
 
+		case Basic_f32le: return LLVMFloatTypeInContext(ctx);
+		case Basic_f64le: return LLVMDoubleTypeInContext(ctx);
+
+		case Basic_f32be: return LLVMFloatTypeInContext(ctx);
+		case Basic_f64be: return LLVMDoubleTypeInContext(ctx);
+
 		// Basic_complex32,
 		case Basic_complex64:
 			{
@@ -4077,7 +4083,11 @@ lbValue lb_const_bool(lbModule *m, Type *type, bool value) {
 }
 
 LLVMValueRef lb_const_f32(lbModule *m, f32 f, Type *type=t_f32) {
+	GB_ASSERT(type_size_of(type) == 4);
 	u32 u = bit_cast<u32>(f);
+	if (is_type_different_to_arch_endianness(type)) {
+		u = gb_endian_swap32(u);
+	}
 	LLVMValueRef i = LLVMConstInt(LLVMInt32TypeInContext(m->ctx), u, false);
 	return LLVMConstBitCast(i, lb_type(m, type));
 }
@@ -4437,7 +4447,13 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value) {
 			res.value = lb_const_f32(m, f, type);
 			return res;
 		}
-		res.value = LLVMConstReal(lb_type(m, original_type), value.value_float);
+		if (is_type_different_to_arch_endianness(type)) {
+			u64 u = bit_cast<u64>(value.value_float);
+			u = gb_endian_swap64(u);
+			res.value = LLVMConstReal(lb_type(m, original_type), bit_cast<f64>(u));
+		} else {
+			res.value = LLVMConstReal(lb_type(m, original_type), value.value_float);
+		}
 		return res;
 	case ExactValue_Complex:
 		{
@@ -4927,6 +4943,16 @@ lbValue lb_emit_unary_arith(lbProcedure *p, TokenKind op, lbValue x, Type *type)
 		return lb_emit_byte_swap(p, res, type);
 	}
 
+	if (op == Token_Sub && is_type_float(type) && is_type_different_to_arch_endianness(type)) {
+		Type *platform_type = integer_endian_type_to_platform_type(type);
+		lbValue v = lb_emit_byte_swap(p, x, platform_type);
+
+		lbValue res = {};
+		res.value = LLVMBuildFNeg(p->builder, v.value, "");
+		res.type = platform_type;
+
+		return lb_emit_byte_swap(p, res, type);
+	}
 
 	lbValue res = {};
 
@@ -5134,6 +5160,16 @@ lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Ty
 		return lb_emit_byte_swap(p, res, type);
 	}
 
+	if (is_type_float(type) && is_type_different_to_arch_endianness(type)) {
+		Type *platform_type = integer_endian_type_to_platform_type(type);
+		lbValue x = lb_emit_conv(p, lhs, integer_endian_type_to_platform_type(lhs.type));
+		lbValue y = lb_emit_conv(p, rhs, integer_endian_type_to_platform_type(rhs.type));
+
+		lbValue res = lb_emit_arith(p, op, x, y, platform_type);
+
+		return lb_emit_byte_swap(p, res, type);
+	}
+
 
 
 handle_op:
@@ -5563,6 +5599,31 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
 		i64 sz = type_size_of(src);
 		i64 dz = type_size_of(dst);
 
+
+		if (dz == sz) {
+			if (types_have_same_internal_endian(src, dst)) {
+				lbValue res = {};
+				res.type = t;
+				res.value = value.value;
+				return res;
+			} else {
+				return lb_emit_byte_swap(p, value, t);
+			}
+		}
+
+		if (is_type_different_to_arch_endianness(src) || is_type_different_to_arch_endianness(dst)) {
+			Type *platform_src_type = integer_endian_type_to_platform_type(src);
+			Type *platform_dst_type = integer_endian_type_to_platform_type(dst);
+			lbValue res = {};
+			res = lb_emit_conv(p, value, platform_src_type);
+			res = lb_emit_conv(p, res, platform_dst_type);
+			if (is_type_different_to_arch_endianness(dst)) {
+				res = lb_emit_byte_swap(p, res, t);
+			}
+			return lb_emit_conv(p, res, t);
+		}
+
+
 		lbValue res = {};
 		res.type = t;
 
@@ -7803,7 +7864,6 @@ LLVMValueRef lb_lookup_runtime_procedure(lbModule *m, String const &name) {
 lbValue lb_emit_byte_swap(lbProcedure *p, lbValue value, Type *platform_type) {
 	Type *vt = core_type(value.type);
 	GB_ASSERT(type_size_of(vt) == type_size_of(platform_type));
-	GB_ASSERT(is_type_integer(vt));
 
 	// TODO(bill): lb_emit_byte_swap
 	lbValue res = {};
@@ -7812,17 +7872,29 @@ lbValue lb_emit_byte_swap(lbProcedure *p, lbValue value, Type *platform_type) {
 
 	int sz = cast(int)type_size_of(vt);
 	if (sz > 1) {
-		String name = {};
-		switch (sz) {
-		case 2:  name = str_lit("bswap_16");  break;
-		case 4:  name = str_lit("bswap_32");  break;
-		case 8:  name = str_lit("bswap_64");  break;
-		case 16: name = str_lit("bswap_128"); break;
-		default: GB_PANIC("unhandled byteswap size"); break;
-		}
-		LLVMValueRef fn = lb_lookup_runtime_procedure(p->module, name);
+		if (is_type_float(platform_type)) {
+			String name = {};
+			switch (sz) {
+			case 4:  name = str_lit("bswap_f32");  break;
+			case 8:  name = str_lit("bswap_f64");  break;
+			default: GB_PANIC("unhandled byteswap size"); break;
+			}
+			LLVMValueRef fn = lb_lookup_runtime_procedure(p->module, name);
+			res.value = LLVMBuildCall(p->builder, fn, &value.value, 1, "");
+		} else {
+			GB_ASSERT(is_type_integer(platform_type));
+			String name = {};
+			switch (sz) {
+			case 2:  name = str_lit("bswap_16");  break;
+			case 4:  name = str_lit("bswap_32");  break;
+			case 8:  name = str_lit("bswap_64");  break;
+			case 16: name = str_lit("bswap_128"); break;
+			default: GB_PANIC("unhandled byteswap size"); break;
+			}
+			LLVMValueRef fn = lb_lookup_runtime_procedure(p->module, name);
 
-		res.value = LLVMBuildCall(p->builder, fn, &value.value, 1, "");
+			res.value = LLVMBuildCall(p->builder, fn, &value.value, 1, "");
+		}
 	}
 
 	return res;
@@ -8248,6 +8320,12 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri
 			lbValue y = lb_emit_byte_swap(p, right, platform_type);
 			left = x;
 			right = y;
+		} else if (is_type_float(t) && is_type_different_to_arch_endianness(t)) {
+			Type *platform_type = integer_endian_type_to_platform_type(t);
+			lbValue x = lb_emit_conv(p, left, platform_type);
+			lbValue y = lb_emit_conv(p, right, platform_type);
+			left = x;
+			right = y;
 		}
 	}
 
@@ -10433,7 +10511,31 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
 			// case Basic_f16:
 			case Basic_f32:
 			case Basic_f64:
-				tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_float_ptr);
+			case Basic_f32le:
+			case Basic_f64le:
+			case Basic_f32be:
+			case Basic_f64be:
+				{
+					tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_float_ptr);
+
+					// 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;
+					}
+					lbValue endianness = lb_const_int(m, t_u8, endianness_value);
+
+					LLVMValueRef vals[1] = {
+						endianness.value,
+					};
+
+					lbValue res = {};
+					res.type = type_deref(tag.type);
+					res.value = LLVMConstNamedStruct(lb_type(m, res.type), vals, gb_count_of(vals));
+					lb_emit_store(p, tag, res);
+				}
 				break;
 
 			// case Basic_complex32: