Browse Source

Add `intrinsics.alloca`

gingerBill 5 years ago
parent
commit
d1d5f61230
7 changed files with 184 additions and 4 deletions
  1. 10 0
      core/intrinsics/intrinsics.odin
  2. 80 0
      core/mem/allocators.odin
  3. 54 0
      src/check_expr.cpp
  4. 3 1
      src/checker_builtin_procs.hpp
  5. 14 3
      src/ir.cpp
  6. 10 0
      src/ir_print.cpp
  7. 13 0
      src/llvm_backend.cpp

+ 10 - 0
core/intrinsics/intrinsics.odin

@@ -1,10 +1,15 @@
 // This is purely for documentation
 package intrinsics
 
+// Types
 
 x86_mmx :: x86_mmx; // Specialized SIMD Vector type
 
 simd_vector :: proc($N: int, $T: typeid) -> type/#simd[N]T
+soa_struct :: proc($N: int, $T: typeid) -> type/#soa[N]T
+
+
+// Atomics
 
 atomic_fence        :: proc() ---
 atomic_fence_acq    :: proc() ---
@@ -78,6 +83,11 @@ atomic_cxchgweak_failacq            :: proc(dst: ^$T, old, new: T) -> (T, /*opti
 atomic_cxchgweak_acq_failrelaxed    :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
 atomic_cxchgweak_acqrel_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
 
+// Instructions
+
+alloca :: proc(size, align: int) -> ^u8 ---
+
+cpu_relax :: proc() ---
 
 // Constant type tests
 

+ 80 - 0
core/mem/allocators.odin

@@ -1,5 +1,7 @@
 package mem
 
+import "intrinsics"
+
 nil_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
                            size, alignment: int,
                            old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
@@ -630,3 +632,81 @@ dynamic_pool_free_all :: proc(using pool: ^Dynamic_Pool) {
 	}
 	clear(&unused_blocks);
 }
+
+
+panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
+                             size, alignment: int,
+                             old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
+
+	switch mode {
+	case .Alloc:
+		if size > 0 {
+			panic("mem: panic allocator, .Alloc called");
+		}
+	case .Resize:
+		if size > 0 {
+			panic("mem: panic allocator, .Resize called");
+		}
+	case .Free:
+		if old_memory != nil {
+			panic("mem: panic allocator, .Free called");
+		}
+	case .Free_All:
+		panic("mem: panic allocator, .Free_All called");
+	}
+
+	return nil;
+}
+
+panic_allocator :: proc() -> Allocator {
+	return Allocator{
+		procedure = panic_allocator_proc,
+		data = nil,
+	};
+}
+
+
+alloca_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
+                              size, alignment: int,
+                              old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
+	switch mode {
+	case .Alloc:
+		switch alignment {
+		case:   return intrinsics.alloca(size, 2*align_of(uintptr));
+		case 0: return intrinsics.alloca(size, 0);
+
+		case 1:     return intrinsics.alloca(size, 1);
+		case 2:     return intrinsics.alloca(size, 2);
+		case 4:     return intrinsics.alloca(size, 4);
+		case 8:     return intrinsics.alloca(size, 8);
+		case 16:    return intrinsics.alloca(size, 16);
+		case 32:    return intrinsics.alloca(size, 32);
+		case 64:    return intrinsics.alloca(size, 64);
+		case 128:   return intrinsics.alloca(size, 128);
+		case 256:   return intrinsics.alloca(size, 256);
+		case 512:   return intrinsics.alloca(size, 512);
+		case 1024:  return intrinsics.alloca(size, 1024);
+		case 2048:  return intrinsics.alloca(size, 2048);
+		case 4096:  return intrinsics.alloca(size, 4096);
+		case 8192:  return intrinsics.alloca(size, 8192);
+		case 16384: return intrinsics.alloca(size, 16384);
+		case 32768: return intrinsics.alloca(size, 32768);
+		case 65536: return intrinsics.alloca(size, 65536);
+		}
+	case .Resize:
+		return default_resize_align(old_memory, old_size, size, alignment, alloca_allocator());
+
+	case .Free:
+		// Do nothing
+	case .Free_All:
+		// Do nothing
+	}
+	return nil;
+}
+
+alloca_allocator :: proc() -> Allocator {
+	return Allocator{
+		procedure = alloca_allocator_proc,
+		data = nil,
+	};
+}

+ 54 - 0
src/check_expr.cpp

@@ -5408,6 +5408,60 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 		break;
 	}
 
+	case BuiltinProc_alloca:
+		{
+			Operand sz = {};
+			Operand al = {};
+
+			check_expr(c, &sz, ce->args[0]);
+			if (sz.mode == Addressing_Invalid) {
+				return false;
+			}
+			check_expr(c, &al, ce->args[1]);
+			if (al.mode == Addressing_Invalid) {
+				return false;
+			}
+			convert_to_typed(c, &sz, t_int); if (sz.mode == Addressing_Invalid) return false;
+			convert_to_typed(c, &al, t_int); if (al.mode == Addressing_Invalid) return false;
+
+			if (!is_type_integer(sz.type) || !is_type_integer(al.type)) {
+				error(operand->expr, "Both parameters to '%.*s' must integers", LIT(builtin_name));
+				return false;
+			}
+
+			if (sz.mode == Addressing_Constant) {
+				i64 i_sz = exact_value_to_i64(sz.value);
+				if (i_sz < 0) {
+					error(sz.expr, "Size parameter to '%.*s' must be non-negative, got %lld", LIT(builtin_name), cast(long long)i_sz);
+					return false;
+				}
+			}
+			if (al.mode == Addressing_Constant) {
+				i64 i_al = exact_value_to_i64(al.value);
+				if (i_al < 0) {
+					error(al.expr, "Alignment parameter to '%.*s' must be non-negative, got %lld", LIT(builtin_name), cast(long long)i_al);
+					return false;
+				}
+
+				if (i_al > 1<<29) {
+					error(al.expr, "Alignment parameter to '%.*s' must not exceed '1<<29', got %lld", LIT(builtin_name), cast(long long)i_al);
+					return false;
+				}
+
+				if (!gb_is_power_of_two(cast(isize)i_al) && i_al != 0) {
+					error(al.expr, "Alignment parameter to '%.*s' must be a power of 2 or 0, got %lld", LIT(builtin_name), cast(long long)i_al);
+					return false;
+				}
+			} else {
+				error(al.expr, "Alignment parameter to '%.*s' must be constant", LIT(builtin_name));
+			}
+
+			operand->type = t_u8_ptr;
+			operand->mode = Addressing_Value;
+			break;
+		}
+
+
 	case BuiltinProc_cpu_relax:
 		operand->mode = Addressing_NoValue;
 		break;

+ 3 - 1
src/checker_builtin_procs.hpp

@@ -36,6 +36,7 @@ enum BuiltinProcId {
 	BuiltinProc_simd_vector,
 	BuiltinProc_soa_struct,
 
+	BuiltinProc_alloca,
 	BuiltinProc_cpu_relax,
 
 	BuiltinProc_atomic_fence,
@@ -220,7 +221,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
 	{STR_LIT("simd_vector"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, // Type
 	{STR_LIT("soa_struct"),  2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, // Type
 
-	{STR_LIT("cpu_relax"),  0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
+	{STR_LIT("alloca"),    2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+	{STR_LIT("cpu_relax"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
 
 	{STR_LIT("atomic_fence"),        0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
 	{STR_LIT("atomic_fence_acq"),    0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},

+ 14 - 3
src/ir.cpp

@@ -200,7 +200,7 @@ gbAllocator ir_allocator(void) {
 	IR_INSTR_KIND(ZeroInit, struct { irValue *address; })             \
 	IR_INSTR_KIND(Store,    struct { irValue *address, *value; bool is_volatile; }) \
 	IR_INSTR_KIND(Load,     struct { Type *type; irValue *address; i64 custom_align; }) \
-	IR_INSTR_KIND(InlineCode, struct { BuiltinProcId id; Array<irValue *> operands; }) \
+	IR_INSTR_KIND(InlineCode, struct { BuiltinProcId id; Array<irValue *> operands; Type *type; }) \
 	IR_INSTR_KIND(AtomicFence, struct { BuiltinProcId id; })          \
 	IR_INSTR_KIND(AtomicStore, struct {                               \
 		irValue *address, *value;                                     \
@@ -741,6 +741,8 @@ gb_inline bool ir_min_dep_entity(irModule *m, Entity *e) {
 
 Type *ir_instr_type(irInstr *instr) {
 	switch (instr->kind) {
+	case irInstr_InlineCode:
+		return instr->InlineCode.type;
 	case irInstr_Local:
 		return instr->Local.type;
 	case irInstr_Load:
@@ -1093,11 +1095,12 @@ irValue *ir_instr_load(irProcedure *p, irValue *address) {
 	return v;
 }
 
-irValue *ir_instr_inline_code(irProcedure *p, BuiltinProcId id, Array<irValue *> operands) {
+irValue *ir_instr_inline_code(irProcedure *p, BuiltinProcId id, Array<irValue *> const &operands, Type *type) {
 	irValue *v = ir_alloc_instr(p, irInstr_InlineCode);
 	irInstr *i = &v->Instr;
 	i->InlineCode.id = id;
 	i->InlineCode.operands = operands;
+	i->InlineCode.type = type;
 	return v;
 }
 
@@ -7287,8 +7290,16 @@ irValue *ir_build_builtin_proc(irProcedure *proc, Ast *expr, TypeAndValue tv, Bu
 
 
 	// "Intrinsics"
+	case BuiltinProc_alloca:
+		{
+			auto args = array_make<irValue *>(heap_allocator(), 2);
+			args[0] = ir_emit_conv(proc, ir_build_expr(proc, ce->args[0]), t_i32);
+			args[1] = ir_build_expr(proc, ce->args[1]);
+			return ir_emit(proc, ir_instr_inline_code(proc, id, args, t_u8_ptr));
+		}
+
 	case BuiltinProc_cpu_relax:
-		return ir_emit(proc, ir_instr_inline_code(proc, id, {}));
+		return ir_emit(proc, ir_instr_inline_code(proc, id, {}, nullptr));
 
 	case BuiltinProc_atomic_fence:
 	case BuiltinProc_atomic_fence_acq:

+ 10 - 0
src/ir_print.cpp

@@ -1539,6 +1539,16 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
 	case irInstr_InlineCode:
 		{
 			switch (instr->InlineCode.id) {
+			case BuiltinProc_alloca:
+				ir_fprintf(f, "%%%d = ", value->index);
+				ir_write_str_lit(f, "alloca i8, ");
+				ir_print_type(f, m, ir_type(instr->InlineCode.operands[0]));
+				ir_write_str_lit(f, " ");
+				ir_print_value(f, m, instr->InlineCode.operands[0], ir_type(instr->InlineCode.operands[0]));
+				ir_write_str_lit(f, ", align ");
+				ir_print_value(f, m, instr->InlineCode.operands[1], t_i32);
+				break;
+
 			case BuiltinProc_cpu_relax:
 				ir_write_str_lit(f, "call void asm sideeffect \"pause\", \"\"()");
 				break;

+ 13 - 0
src/llvm_backend.cpp

@@ -7778,6 +7778,19 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
 
 
 	// "Intrinsics"
+
+	case BuiltinProc_alloca:
+		{
+			lbValue sz = lb_build_expr(p, ce->args[0]);
+			i64 al = exact_value_to_i64(type_and_value_of_expr(ce->args[1]).value);
+
+			lbValue res = {};
+			res.type = t_u8_ptr;
+			res.value = LLVMBuildArrayAlloca(p->builder, lb_type(p->module, t_u8), sz.value, "");
+			LLVMSetAlignment(res.value, cast(unsigned)al);
+			return res;
+		}
+
 	case BuiltinProc_cpu_relax:
 		// TODO(bill): BuiltinProc_cpu_relax
 		// ir_write_str_lit(f, "call void asm sideeffect \"pause\", \"\"()");