Merge remote-tracking branch 'origin/trunk' into list-unsafe-append

This commit is contained in:
Folkert 2022-07-08 13:57:09 +02:00
commit 972d652d5d
No known key found for this signature in database
GPG Key ID: 1F17F6FFD112B97C
7 changed files with 10 additions and 261 deletions

View File

@ -1,134 +0,0 @@
const std = @import("std");
const utils = @import("utils.zig");
const CSlice = utils.CSlice;
const always_inline = std.builtin.CallOptions.Modifier.always_inline;
const Failure = struct {
start_line: u32,
end_line: u32,
start_col: u16,
end_col: u16,
};
// BEGIN FAILURES GLOBALS ///////////////////
var failures_mutex = std.Thread.Mutex{};
var failures: [*]Failure = undefined;
var failure_length: usize = 0;
var failure_capacity: usize = 0;
// END FAILURES GLOBALS /////////////////////
pub fn expectFailed(
start_line: u32,
end_line: u32,
start_col: u16,
end_col: u16,
) void {
const new_failure = Failure{ .start_line = start_line, .end_line = end_line, .start_col = start_col, .end_col = end_col };
// Lock the failures mutex before reading from any of the failures globals,
// and then release the lock once we're done modifying things.
failures_mutex.lock();
defer failures_mutex.unlock();
// If we don't have enough capacity to add a failure, allocate a new failures pointer.
if (failure_length >= failure_capacity) {
if (failure_capacity > 0) {
// We already had previous failures allocated, so try to realloc in order
// to grow the size in-place without having to memcpy bytes over.
const old_pointer = failures;
const old_bytes = failure_capacity * @sizeOf(Failure);
failure_capacity *= 2;
const new_bytes = failure_capacity * @sizeOf(Failure);
const failures_u8 = @ptrCast([*]u8, @alignCast(@alignOf(Failure), failures));
const raw_pointer = utils.realloc(failures_u8, new_bytes, old_bytes, @alignOf(Failure));
failures = @ptrCast([*]Failure, @alignCast(@alignOf(Failure), raw_pointer));
// If realloc wasn't able to expand in-place (that is, it returned a different pointer),
// then copy the data into the new pointer and dealloc the old one.
if (failures != old_pointer) {
const old_pointer_u8 = @ptrCast([*]u8, old_pointer);
utils.memcpy(@ptrCast([*]u8, failures), old_pointer_u8, old_bytes);
utils.dealloc(old_pointer_u8, @alignOf(Failure));
}
} else {
// We've never had any failures before, so allocate the failures for the first time.
failure_capacity = 10;
const raw_pointer = utils.alloc(failure_capacity * @sizeOf(Failure), @alignOf(Failure));
failures = @ptrCast([*]Failure, @alignCast(@alignOf(Failure), raw_pointer));
}
}
failures[failure_length] = new_failure;
failure_length += 1;
}
pub fn expectFailedC(
start_line: u32,
end_line: u32,
start_col: u16,
end_col: u16,
) callconv(.C) void {
return @call(.{ .modifier = always_inline }, expectFailed, .{ start_line, end_line, start_col, end_col });
}
pub fn getExpectFailures() []Failure {
failures_mutex.lock();
defer failures_mutex.unlock();
if (failure_length > 0) {
// defensively clone failures, in case someone modifies the originals after the mutex has been released.
const num_bytes = failure_length * @sizeOf(Failure);
// TODO handle the possibility of alloc failing
const raw_clones = utils.alloc(num_bytes, @alignOf(Failure)) orelse unreachable;
utils.memcpy(raw_clones, @ptrCast([*]u8, failures), num_bytes);
const clones = @ptrCast([*]Failure, @alignCast(@alignOf(Failure), raw_clones));
return clones[0..failure_length];
} else {
return failures[0..0];
}
}
pub fn getExpectFailuresC() callconv(.C) CSlice {
var bytes = @ptrCast(*anyopaque, failures);
return .{ .pointer = bytes, .len = failure_length };
}
pub fn deinitFailures() void {
failures_mutex.lock();
defer failures_mutex.unlock();
utils.dealloc(@ptrCast([*]u8, failures), @alignOf(Failure));
failure_length = 0;
}
pub fn deinitFailuresC() callconv(.C) void {
return @call(.{ .modifier = always_inline }, deinitFailures, .{});
}
test "expectFailure does something" {
defer deinitFailures();
var fails = getExpectFailures();
try std.testing.expectEqual(fails.len, 0);
expectFailed(1, 2, 3, 4);
fails = getExpectFailures();
try std.testing.expectEqual(fails.len, 1);
utils.dealloc(@ptrCast([*]u8, fails.ptr), @alignOf([*]Failure));
const what_it_should_look_like = Failure{ .start_line = 1, .end_line = 2, .start_col = 3, .end_col = 4 };
fails = getExpectFailures();
try std.testing.expectEqual(fails[0], what_it_should_look_like);
utils.dealloc(@ptrCast([*]u8, fails.ptr), @alignOf([*]Failure));
}

View File

@ -2,7 +2,6 @@ const std = @import("std");
const builtin = @import("builtin");
const math = std.math;
const utils = @import("utils.zig");
const expect = @import("expect.zig");
const ROC_BUILTINS = "roc_builtins";
const NUM = "num";
@ -185,9 +184,6 @@ comptime {
exportUtilsFn(utils.decrefC, "decref");
exportUtilsFn(utils.decrefCheckNullC, "decref_check_null");
exportUtilsFn(utils.allocateWithRefcountC, "allocate_with_refcount");
exportExpectFn(expect.expectFailedC, "expect_failed");
exportExpectFn(expect.getExpectFailuresC, "get_expect_failures");
exportExpectFn(expect.deinitFailuresC, "deinit_failures");
@export(utils.panic, .{ .name = "roc_builtins.utils." ++ "panic", .linkage = .Weak });
@ -241,10 +237,6 @@ fn exportUtilsFn(comptime func: anytype, comptime func_name: []const u8) void {
exportBuiltinFn(func, "utils." ++ func_name);
}
fn exportExpectFn(comptime func: anytype, comptime func_name: []const u8) void {
exportBuiltinFn(func, "expect." ++ func_name);
}
// Custom panic function, as builtin Zig version errors during LLVM verification
pub fn panic(message: []const u8, stacktrace: ?*std.builtin.StackTrace) noreturn {
if (builtin.is_test) {

View File

@ -393,9 +393,6 @@ pub const UTILS_ALLOCATE_WITH_REFCOUNT: &str = "roc_builtins.utils.allocate_with
pub const UTILS_INCREF: &str = "roc_builtins.utils.incref";
pub const UTILS_DECREF: &str = "roc_builtins.utils.decref";
pub const UTILS_DECREF_CHECK_NULL: &str = "roc_builtins.utils.decref_check_null";
pub const UTILS_EXPECT_FAILED: &str = "roc_builtins.expect.expect_failed";
pub const UTILS_GET_EXPECT_FAILURES: &str = "roc_builtins.expect.get_expect_failures";
pub const UTILS_DEINIT_FAILURES: &str = "roc_builtins.expect.deinit_failures";
pub const UTILS_LONGJMP: &str = "longjmp";
pub const UTILS_SETJMP: &str = "setjmp";

View File

@ -39,7 +39,7 @@ use inkwell::types::{
};
use inkwell::values::BasicValueEnum::{self, *};
use inkwell::values::{
BasicMetadataValueEnum, BasicValue, CallSiteValue, CallableValue, FloatValue, FunctionValue,
BasicMetadataValueEnum, BasicValue, CallSiteValue, FloatValue, FunctionValue,
InstructionOpcode, InstructionValue, IntValue, PhiValue, PointerValue, StructValue,
};
use inkwell::OptimizationLevel;
@ -65,7 +65,7 @@ use roc_mono::layout::{
};
use roc_std::RocDec;
use roc_target::{PtrWidth, TargetInfo};
use std::convert::{TryFrom, TryInto};
use std::convert::TryInto;
use std::path::Path;
use target_lexicon::{Architecture, OperatingSystem, Triple};
@ -2779,30 +2779,8 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
match env.target_info.ptr_width() {
roc_target::PtrWidth::Bytes8 => {
let func = env
.module
.get_function(bitcode::UTILS_EXPECT_FAILED)
.unwrap();
// TODO get the actual line info instead of
// hardcoding as zero!
let callable = CallableValue::try_from(func).unwrap();
let start_line = context.i32_type().const_int(0, false);
let end_line = context.i32_type().const_int(0, false);
let start_col = context.i16_type().const_int(0, false);
let end_col = context.i16_type().const_int(0, false);
bd.build_call(
callable,
&[
start_line.into(),
end_line.into(),
start_col.into(),
end_col.into(),
],
"call_expect_failed",
);
bd.build_unconditional_branch(then_block);
// temporary native implementation
throw_exception(env, "An expectation failed!");
}
roc_target::PtrWidth::Bytes4 => {
// temporary WASM implementation

View File

@ -47,43 +47,6 @@ pub fn add_default_roc_externs(env: &Env<'_, '_, '_>) {
}
}
// roc_memcpy
{
// The type of this function (but not the implementation) should have
// already been defined by the builtins, which rely on it.
let fn_val = module.get_function("roc_memcpy").unwrap();
let mut params = fn_val.get_param_iter();
let dest_arg = params.next().unwrap();
let dest_alignment = 1;
let src_arg = params.next().unwrap();
let src_alignment = 1;
let bytes_arg = params.next().unwrap();
debug_assert!(params.next().is_none());
// Add a basic block for the entry point
let entry = ctx.append_basic_block(fn_val, "entry");
builder.position_at_end(entry);
// Call libc memcpy()
let _retval = builder
.build_memcpy(
dest_arg.into_pointer_value(),
dest_alignment,
src_arg.into_pointer_value(),
src_alignment,
bytes_arg.into_int_value(),
)
.unwrap();
builder.build_return(None);
if cfg!(debug_assertions) {
crate::llvm::build::verify_fn(fn_val);
}
}
// roc_realloc
{
let libc_realloc_val = {

View File

@ -89,15 +89,6 @@ macro_rules! run_jit_function {
use roc_gen_llvm::run_roc::RocCallResult;
use std::mem::MaybeUninit;
#[derive(Debug, Copy, Clone)]
#[repr(C)]
struct Failure {
start_line: u32,
end_line: u32,
start_col: u16,
end_col: u16,
}
unsafe {
let main: libloading::Symbol<unsafe extern "C" fn(*mut RocCallResult<$ty>)> = $lib
.get($main_fn_name.as_bytes())
@ -105,48 +96,8 @@ macro_rules! run_jit_function {
.ok_or(format!("Unable to JIT compile `{}`", $main_fn_name))
.expect("errored");
#[repr(C)]
struct Failures {
failures: *const Failure,
count: usize,
}
impl Drop for Failures {
fn drop(&mut self) {
use std::alloc::{dealloc, Layout};
use std::mem;
unsafe {
let layout = Layout::from_size_align_unchecked(
mem::size_of::<Failure>(),
mem::align_of::<Failure>(),
);
dealloc(self.failures as *mut u8, layout);
}
}
}
let get_expect_failures: libloading::Symbol<unsafe extern "C" fn() -> Failures> = $lib
.get(bitcode::UTILS_GET_EXPECT_FAILURES.as_bytes())
.ok()
.ok_or(format!(
"Unable to JIT compile `{}`",
bitcode::UTILS_GET_EXPECT_FAILURES
))
.expect("errored");
let mut main_result = MaybeUninit::uninit();
main(main_result.as_mut_ptr());
let failures = get_expect_failures();
if failures.count > 0 {
// TODO tell the user about the failures!
let failures =
unsafe { core::slice::from_raw_parts(failures.failures, failures.count) };
panic!("Failed with {} failures. Failures: ", failures.len());
}
match main_result.assume_init().into() {
Ok(success) => {

View File

@ -1431,8 +1431,9 @@ fn issue_2365_monomorphize_tag_with_non_empty_ext_var_wrapped() {
main = compound {}
"#
),
2, // C
u8
(0, 2), // Err, C
([u8; std::mem::size_of::<RocStr>()], u8),
|(err_tag, wrap_tag): ([u8; std::mem::size_of::<RocStr>()], u8)| (wrap_tag, err_tag[0])
)
}
@ -1460,8 +1461,9 @@ fn issue_2365_monomorphize_tag_with_non_empty_ext_var_wrapped_nested() {
compound {}
"#
),
2, // C
u8
(0, 2), // Err, C
([u8; std::mem::size_of::<RocStr>()], u8),
|(err_tag, wrap_tag): ([u8; std::mem::size_of::<RocStr>()], u8)| (wrap_tag, err_tag[0])
)
}