summaryrefslogtreecommitdiff
path: root/foreign_msvc64.c
blob: 4bbb6432dfd8bd1d0aa9bb0ae14776d85d1d6acb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#if SIZE_MAX != U64_MAX
#error "What's going on? The 64-bit Windows file was included, but size_t isn't 64 bits!"
#endif

extern U64 win64_call(FnPtr fn, U64 *args, I64 nargs);
extern float win64_callf(FnPtr fn, U64 *args, I64 nargs);
extern double win64_calld(FnPtr fn, U64 *args, I64 nargs);

static Status val_to_word(Value v, Type *t, Location where, U64 *w) {
	switch (t->kind) {
	case TYPE_BUILTIN:
		switch (t->builtin) {
		case BUILTIN_I8: *w = (U64)v.i8; break;
		case BUILTIN_I16: *w = (U64)v.i16; break;
		case BUILTIN_I32: *w = (U64)v.i32; break;
		case BUILTIN_U8: *w = (U64)v.u8; break;
		case BUILTIN_U16: *w = (U64)v.u16; break;
		case BUILTIN_U32: *w = (U64)v.u32; break;
		case BUILTIN_I64: *w = (U64)v.i64; break;
		case BUILTIN_U64: *w = v.u64; break;
		case BUILTIN_F32: *w = (U64)*(U32 *)&v.f32; break;
		case BUILTIN_F64: *w = *(U64 *)&v.f64; break;
		case BUILTIN_CHAR: *w = (U64)v.charv; break;
		case BUILTIN_BOOL: *w = (U64)v.boolv; break;
		case BUILTIN_TYPE:
		case BUILTIN_VARARGS:
		case BUILTIN_NMS:
		case BUILTIN_VOID:
			goto unsupported;
		}
		break;
	case TYPE_PTR:
		*w = (U64)v.ptr; break;
	default:
	unsupported: {
		/* @TODO(eventually) */
		char *s = type_to_str(t);
		err_print(where, "#foreign functions can't take arguments of type %s at compile time on Windows.", s);
		free(s);
		return false;
	}
	}
	return true;
}

static Status foreign_call(ForeignFnManager *ffmgr, FnExpr *fn, Type *ret_type, Type *arg_types, size_t arg_types_stride, Value *args, size_t nargs, Location call_where, Value *ret) {
	possibly_static_assert(sizeof(double) == 8);
	possibly_static_assert(sizeof(float) == 4);
	FnPtr fn_ptr = msvc_get_fn_ptr(ffmgr, fn, call_where);

	U64 *words = err_malloc(nargs * sizeof *words);
	U64 *word = words;
	char *type = (char *)arg_types;
	for (size_t i = 0; i < nargs; ++i) {
		if (!val_to_words(args[i], (Type *)type, call_where, word))
			return false;
		type += arg_types_stride;
		++word;
	}
	int kind = 0; /* 0=>integer, 1=>f32, 2=>f64 */
	switch (ret_type->kind) {
	case TYPE_BUILTIN:
		switch (ret_type->builtin) {
		case BUILTIN_I8: 
		case BUILTIN_I16:
		case BUILTIN_I32:
		case BUILTIN_I64:
		case BUILTIN_U8: 
		case BUILTIN_U16:
		case BUILTIN_U32:
		case BUILTIN_U64:
		case BUILTIN_BOOL:
		case BUILTIN_CHAR:
		case BUILTIN_VOID:
			break;
		case BUILTIN_F32:
			kind = 1;
			break;
		case BUILTIN_F64:
			kind = 2;
			break;
		default:
			goto unsupported;
		}
		break;
	case TYPE_PTR:
		break;
	default:
	unsupported: {
		char *s = type_to_str(ret_type);
		/* @TODO(eventually) */
		err_print(call_where, "You can't call functions which return type %s at compile time on Windows.", s);
		free(s);
		return false;
	}
	}
	
	switch (kind) {
	case 0: {
		U64 r = win64_call(fn_ptr, words, (I64)nargs);
		switch (ret_type->kind) {
		case TYPE_BUILTIN:
			switch (ret_type->builtin) {
			case BUILTIN_I8: ret->i8 = (I8)r; break;
			case BUILTIN_I16: ret->i16 = (I16)r; break;
			case BUILTIN_I32: ret->i32 = (I32)r; break;
			case BUILTIN_I64: ret->i64 = (I64)r; break;
			case BUILTIN_U8: ret->u8 = (U8)r; break;
			case BUILTIN_U16: ret->u16 = (U16)r; break;
			case BUILTIN_U32: ret->u32 = (U32)r; break;
			case BUILTIN_U64: ret->u64 = (U64)r; break;
			case BUILTIN_BOOL: ret->boolv = (bool)r; break;
			case BUILTIN_CHAR: ret->charv = (char)r; break;
			case BUILTIN_VOID: (void)r; break;
			default: assert(0); break;
			}
			break;
		case TYPE_PTR:
			ret->ptr = (void *)r;
			break;
		default: assert(0); break;
		}
	} break;
	case 1:
		ret->f32 = win64_callf(fn_ptr, words, (I64)nargs);
		break;
	case 2:
		ret->f64 = win64_calld(fn_ptr, words, (I64)nargs);
		break;
	}
	free(words);
	return true;
}