Merge branch 'main' into markdown

Signed-off-by: Anton-4 <17049058+Anton-4@users.noreply.github.com>
This commit is contained in:
Anton-4 2022-09-06 08:31:49 +02:00 committed by GitHub
commit 322ae187bc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
34 changed files with 558 additions and 288 deletions

View file

@ -1,6 +1,6 @@
on:
schedule:
- cron: '0 9 * * 1' # 9=9am utc+0, 1=monday
- cron: '0 9 * * *' # 9=9am utc+0
name: Nightly Release macOS x86_64
@ -9,8 +9,8 @@ env:
LLVM_SYS_130_PREFIX: /usr/local/opt/llvm
jobs:
test-and-build:
name: Rust tests, build and package nightly release
test-build-upload:
name: build, test, package and upload nightly release
runs-on: [macos-12]
timeout-minutes: 90
steps:
@ -24,30 +24,42 @@ jobs:
run: zig version
- name: Install LLVM
run: brew install llvm@13
# build has to be done before tests #2572
- name: build release
uses: actions-rs/cargo@v1
with:
command: build
args: --release --locked
- name: execute rust tests
uses: actions-rs/cargo@v1
with:
command: test
args: --locked # no --release yet until #3166 is fixed
args: --release --locked -- --skip opaque_wrap_function --skip bool_list_literal
- name: get commit SHA
run: echo "SHA=$(git rev-parse --short "$GITHUB_SHA")" >> $GITHUB_ENV
- name: get date
run: echo "DATE=$(date "+%Y-%m-%d")" >> $GITHUB_ENV
- name: build file name
env:
DATE: ${{ env.DATE }}
SHA: ${{ env.SHA }}
run: echo "RELEASE_TAR_FILENAME=roc_nightly-macos_x86_64-$DATE-$SHA.tar.gz" >> $GITHUB_ENV
- name: write version to file
run: ./ci/write_version.sh
- name: package release
run: ./ci/package_release.sh roc_darwin_x86_64.tar.gz
- name: Create pre-release with test_archive.tar.gz
uses: Anton-4/deploy-nightly@1609d8dfe211b078674801113ab7a2ec2938b2a9
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # automatically provided by github actions
run: ./ci/package_release.sh ${{ env.RELEASE_TAR_FILENAME }}
- name: Upload artifact. Actually uploading to github releases has to be done manually.
uses: actions/upload-artifact@v3
with:
upload_url: https://uploads.github.com/repos/roc-lang/roc/releases/51880579/assets{?name,label}
release_id: 51880579
asset_path: ./roc_darwin_x86_64.tar.gz
asset_name: roc_nightly-macos_x86_64-$$.tar.gz # $$ inserts 6 char commit hash and date (YYYY-MM-DD)
asset_content_type: application/gzip
max_releases: 3
name: ${{ env.RELEASE_TAR_FILENAME }}
path: ${{ env.RELEASE_TAR_FILENAME }}
retention-days: 4

View file

@ -12,7 +12,7 @@ env:
jobs:
spell-check:
name: spell check
runs-on: [self-hosted, i7-6700K]
runs-on: [self-hosted]
timeout-minutes: 10
env:
FORCE_COLOR: 1
@ -21,8 +21,5 @@ jobs:
with:
clean: "true"
- name: Earthly version
run: earthly --version
- name: install spell checker, do spell check
run: ./ci/safe-earthly.sh +check-typos
- name: do spell check with typos-cli 1.0.11 # to reproduce locally: cargo install typos-cli --version 1.0.11
run: typos

View file

@ -5,7 +5,7 @@ on:
name: Test latest nightly release for macOS Apple Silicon
jobs:
test-and-build:
test-nightly:
name: test nightly macos aarch64
runs-on: [self-hosted, macOS, ARM64]
timeout-minutes: 90
@ -16,7 +16,7 @@ jobs:
run: curl https://api.github.com/repos/roc-lang/roc/releases > roc_releases.json
- name: get the url of today`s release for macos apple silicon
run: echo "RELEASE_URL=$(./ci/get_latest_release_url.sh)" >> $GITHUB_ENV
run: echo "RELEASE_URL=$(./ci/get_latest_release_url.sh silicon)" >> $GITHUB_ENV
- name: get the archive from the url
run: curl -OL ${{ env.RELEASE_URL }}

View file

@ -0,0 +1,34 @@
on:
schedule:
- cron: '0 13 * * *'
name: Test latest nightly release for macOS x86_64
jobs:
test-nightly:
name: test nightly macos x86_64
runs-on: [ macos-12 ]
timeout-minutes: 90
steps:
- uses: actions/checkout@v3
- name: fetch releases data and save to file
run: curl https://api.github.com/repos/roc-lang/roc/releases > roc_releases.json
- name: get the url of today`s release for macos apple silicon
run: echo "RELEASE_URL=$(./ci/get_latest_release_url.sh macos_x86_64)" >> $GITHUB_ENV
- name: get the archive from the url
run: curl -OL ${{ env.RELEASE_URL }}
- name: remove everything in this dir except the tar # we want to test like a user who would have downloaded the release, so we clean up all files from the repo checkout
run: ls | grep -v "roc_nightly.*tar\.gz" | xargs rm -rf
- name: decompress the tar
run: ls | grep "roc_nightly.*tar\.gz" | xargs tar -xzvf
- name: test roc hello world
run: ./roc examples/hello-world/main.roc

4
Cargo.lock generated
View file

@ -1638,9 +1638,9 @@ dependencies = [
[[package]]
name = "glyph_brush"
version = "0.7.4"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a69c65dd1f1fbb6209aa00f78636e436ad0a55b7d8e5de886d00720dcad9c6e2"
checksum = "ac02497410cdb5062cc056a33f2e1e19ff69fbf26a4be9a02bf29d6e17ea105b"
dependencies = [
"glyph_brush_draw_cache",
"glyph_brush_layout",

View file

@ -52,84 +52,6 @@ copy-dirs:
FROM +install-zig-llvm-valgrind
COPY --dir crates examples Cargo.toml Cargo.lock version.txt www ./
test-zig:
FROM +install-zig-llvm-valgrind
COPY --dir crates/compiler/builtins/bitcode ./
RUN cd bitcode && ./run-tests.sh && ./run-wasm-tests.sh
build-rust-test:
FROM +copy-dirs
RUN echo "deb http://deb.debian.org/debian testing main contrib non-free" >> /etc/apt/sources.list # to get gcc 10.3
RUN apt -y update
RUN apt -y install gcc-10 g++-10 && rm /usr/bin/gcc && ln -s /usr/bin/gcc-10 /usr/bin/gcc # gcc-9 maybe causes segfault
RUN gcc --version
RUN --mount=type=cache,target=$SCCACHE_DIR \
cargo test --locked --release --features with_sound serde --workspace --no-run && sccache --show-stats
check-typos:
RUN cargo install typos-cli --version 1.0.11 # version set to prevent confusion if the version is updated automatically
COPY --dir .github ci crates examples nightly_benches www *.md LEGAL_DETAILS flake.nix version.txt ./
RUN typos
test-rust:
FROM +build-rust-test
ENV ROC_WORKSPACE_DIR=/earthbuild
ENV RUST_BACKTRACE=1
# for race condition problem with cli test
ENV ROC_NUM_WORKERS=1
# run one of the benchmarks to make sure the host is compiled
# not pre-compiling the host can cause race conditions
RUN gcc --version
RUN echo "4" | cargo run --release examples/benchmarks/NQueens.roc
RUN --mount=type=cache,target=$SCCACHE_DIR \
cargo test --locked --release --features with_sound serde --workspace && sccache --show-stats
# test the dev and wasm backend: they require an explicit feature flag.
RUN --mount=type=cache,target=$SCCACHE_DIR \
cargo test --locked --release --package test_gen --no-default-features --features gen-dev && sccache --show-stats
# gen-wasm has some multithreading problems to do with the wasmer runtime. Run it single-threaded as a separate job
RUN --mount=type=cache,target=$SCCACHE_DIR \
cargo test --locked --release --package test_gen --no-default-features --features gen-wasm -- --test-threads=1 && sccache --show-stats
# run `roc test` on Str builtins
RUN --mount=type=cache,target=$SCCACHE_DIR \
cargo run --release -- test crates/compiler/builtins/roc/Str.roc && sccache --show-stats
# repl_test: build the compiler for wasm target, then run the tests on native target
RUN --mount=type=cache,target=$SCCACHE_DIR \
crates/repl_test/test_wasm.sh && sccache --show-stats
# run i386 (32-bit linux) cli tests
# NOTE: disabled until zig 0.9
# RUN echo "4" | cargo run --locked --release --features="target-x86" -- --target=x86_32 examples/benchmarks/NQueens.roc
# RUN --mount=type=cache,target=$SCCACHE_DIR \
# cargo test --locked --release --features with_sound serde --test cli_run i386 --features="i386-cli-run" && sccache --show-stats
# make sure website deployment works (that is, make sure build.sh returns status code 0)
ENV REPL_DEBUG=1
RUN bash www/build.sh
verify-no-git-changes:
FROM +test-rust
# If running tests caused anything to be changed or added (without being
# included in a .gitignore somewhere), fail the build!
#
# How it works: the `git ls-files` command lists all the modified or
# uncommitted files in the working tree, the `| grep -E .` command returns a
# zero exit code if it listed any files and nonzero otherwise (which is the
# opposite of what we want), and the `!` at the start inverts the exit code.
RUN ! git ls-files --deleted --modified --others --exclude-standard | grep -E .
test-all:
BUILD +test-zig
BUILD +test-rust
BUILD +verify-no-git-changes
build-nightly-release:
FROM +test-rust
COPY --dir .git LICENSE LEGAL_DETAILS ci ./
# version.txt is used by the CLI: roc --version
RUN ./ci/write_version.sh
RUN RUSTFLAGS="-C target-cpu=x86-64" cargo build --features with_sound --release
RUN ./ci/package_release.sh roc_linux_x86_64.tar.gz
SAVE ARTIFACT ./roc_linux_x86_64.tar.gz AS LOCAL roc_linux_x86_64.tar.gz
# compile everything needed for benchmarks and output a self-contained dir from which benchmarks can be run.
prep-bench-folder:
FROM +copy-dirs

View file

@ -2,7 +2,7 @@
# assumes roc_releases.json is present
LATEST_RELEASE_URL=`cat roc_releases.json | jq --arg today $(date +'%Y-%m-%d') '.[0] | .assets | map(.browser_download_url) | map(select(. | contains("silicon-\($today)"))) | .[0]'`
LATEST_RELEASE_URL=`cat roc_releases.json | jq --arg arch $1 --arg today $(date +'%Y-%m-%d') '.[0] | .assets | map(.browser_download_url) | map(select(. | contains("\($arch)-\($today)"))) | .[0]'`
if [[ "$LATEST_RELEASE_URL" == "null" ]]
then

View file

@ -386,7 +386,7 @@ mod cli_run {
// We exclude the C platforming switching example
// because the main platform switching example runs the c platform.
// If we don't a race condition leads to test flakiness.
// platformSwitchingC:"platform-switching/c-platform" => Example {
// platformSwitchingC:"platform-switching" => Example {
// filename: "rocLovesC.roc",
// executable_filename: "rocLovesC",
// stdin: &[],
@ -394,7 +394,7 @@ mod cli_run {
// expected_ending:"Roc <3 C!\n",
// use_valgrind: true,
// },
platformSwitchingRust:"platform-switching/rust-platform" => Example {
platformSwitchingRust:"platform-switching" => Example {
filename: "rocLovesRust.roc",
executable_filename: "rocLovesRust",
stdin: &[],
@ -402,7 +402,7 @@ mod cli_run {
expected_ending:"Roc <3 Rust!\n",
use_valgrind: true,
},
platformSwitchingSwift:"platform-switching/swift-platform" => Example {
platformSwitchingSwift:"platform-switching" => Example {
filename: "rocLovesSwift.roc",
executable_filename: "rocLovesSwift",
stdin: &[],
@ -410,7 +410,7 @@ mod cli_run {
expected_ending:"Roc <3 Swift!\n",
use_valgrind: true,
},
platformSwitchingWebAssembly:"platform-switching/web-assembly-platform" => Example {
platformSwitchingWebAssembly:"platform-switching" => Example {
filename: "rocLovesWebAssembly.roc",
executable_filename: "rocLovesWebAssembly",
stdin: &[],
@ -418,7 +418,7 @@ mod cli_run {
expected_ending:"Roc <3 Web Assembly!\n",
use_valgrind: true,
},
platformSwitchingZig:"platform-switching/zig-platform" => Example {
platformSwitchingZig:"platform-switching" => Example {
filename: "rocLovesZig.roc",
executable_filename: "rocLovesZig",
stdin: &[],
@ -821,25 +821,6 @@ mod cli_run {
if entry.file_type().unwrap().is_dir() {
let example_dir_name = entry.file_name().into_string().unwrap();
// TODO: Improve this with a more-dynamic approach. (Read all subdirectories?)
// Some platform-switching examples live in nested directories
if example_dir_name == "platform-switching" {
for sub_dir in [
// We exclude the C platforming switching example
// because the main platform switching example runs the c platform.
// If we don't a race condition leads to test flakiness.
// "c-platform",
"rust-platform",
"swift-platform",
"web-assembly-platform",
"zig-platform",
] {
all_examples.remove(format!("{}/{}", example_dir_name, sub_dir).as_str()).unwrap_or_else(|| {
panic!("The example directory {}/{}/{} does not have any corresponding tests in cli_run. Please add one, so if it ever stops working, we'll know about it right away!", examples_dir, example_dir_name, sub_dir);
});
}
}
// We test benchmarks separately
if example_dir_name != "benchmarks" {
all_examples.remove(example_dir_name.as_str()).unwrap_or_else(|| {

View file

@ -148,9 +148,12 @@ pub const RocList = extern struct {
) RocList {
if (self.bytes) |source_ptr| {
if (self.isUnique()) {
const new_source = utils.unsafeReallocate(source_ptr, alignment, self.len(), new_length, element_width);
return RocList{ .bytes = new_source, .length = new_length, .capacity = new_length };
if (self.capacity >= new_length) {
return RocList{ .bytes = self.bytes, .length = new_length, .capacity = self.capacity };
} else {
const new_source = utils.unsafeReallocate(source_ptr, alignment, self.len(), new_length, element_width);
return RocList{ .bytes = new_source, .length = new_length, .capacity = new_length };
}
}
}
@ -727,17 +730,20 @@ pub fn listConcat(list_a: RocList, list_b: RocList, alignment: u32, element_widt
return list_b;
} else if (list_b.isEmpty()) {
return list_a;
} else if (!list_a.isEmpty() and list_a.isUnique()) {
} else if (list_a.isUnique()) {
const total_length: usize = list_a.len() + list_b.len();
if (list_a.bytes) |source| {
const new_source = utils.unsafeReallocate(
source,
alignment,
list_a.len(),
total_length,
element_width,
);
const new_source = if (list_a.capacity >= total_length)
source
else
utils.unsafeReallocate(
source,
alignment,
list_a.len(),
total_length,
element_width,
);
if (list_b.bytes) |source_b| {
@memcpy(new_source + list_a.len() * element_width, source_b, list_b.len() * element_width);

View file

@ -215,11 +215,14 @@ pub const RocStr = extern struct {
return result;
}
// NOTE: returns false for empty string!
pub fn isSmallStr(self: RocStr) bool {
return @bitCast(isize, self.str_capacity) < 0;
}
test "isSmallStr: returns true for empty string" {
try expect(isSmallStr(RocStr.empty()));
}
fn asArray(self: RocStr) [@sizeOf(RocStr)]u8 {
const as_ptr = @ptrCast([*]const u8, &self);
const slice = as_ptr[0..@sizeOf(RocStr)];
@ -1652,17 +1655,17 @@ pub fn strToUtf8C(arg: RocStr) callconv(.C) RocList {
}
inline fn strToBytes(arg: RocStr) RocList {
if (arg.isEmpty()) {
const length = arg.len();
if (length == 0) {
return RocList.empty();
} else if (arg.isSmallStr()) {
const length = arg.len();
const ptr = utils.allocateWithRefcount(length, RocStr.alignment);
@memcpy(ptr, arg.asU8ptr(), length);
return RocList{ .length = length, .bytes = ptr, .capacity = length };
} else {
return RocList{ .length = arg.len(), .bytes = arg.str_bytes, .capacity = arg.str_capacity };
return RocList{ .length = length, .bytes = arg.str_bytes, .capacity = arg.str_capacity };
}
}

View file

@ -254,7 +254,7 @@ pub fn unsafeReallocate(
const old_width = align_width + old_length * element_width;
const new_width = align_width + new_length * element_width;
if (old_width == new_width) {
if (old_width >= new_width) {
return source_ptr;
}

View file

@ -834,6 +834,33 @@ impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
fn ret(buf: &mut Vec<'_, u8>) {
ret_reg64(buf, AArch64GeneralReg::LR)
}
fn and_reg64_reg64_reg64(
_buf: &mut Vec<'_, u8>,
_dst: AArch64GeneralReg,
_src1: AArch64GeneralReg,
_src2: AArch64GeneralReg,
) {
todo!("bitwise and for AArch64")
}
fn or_reg64_reg64_reg64(
_buf: &mut Vec<'_, u8>,
_dst: AArch64GeneralReg,
_src1: AArch64GeneralReg,
_src2: AArch64GeneralReg,
) {
todo!("bitwise or for AArch64")
}
fn xor_reg64_reg64_reg64(
_buf: &mut Vec<'_, u8>,
_dst: AArch64GeneralReg,
_src1: AArch64GeneralReg,
_src2: AArch64GeneralReg,
) {
todo!("bitwise xor for AArch64")
}
}
impl AArch64Assembler {}

View file

@ -143,6 +143,27 @@ pub trait Assembler<GeneralReg: RegTrait, FloatReg: RegTrait>: Sized + Copy {
src2: GeneralReg,
);
fn and_reg64_reg64_reg64(
buf: &mut Vec<'_, u8>,
dst: GeneralReg,
src1: GeneralReg,
src2: GeneralReg,
);
fn or_reg64_reg64_reg64(
buf: &mut Vec<'_, u8>,
dst: GeneralReg,
src1: GeneralReg,
src2: GeneralReg,
);
fn xor_reg64_reg64_reg64(
buf: &mut Vec<'_, u8>,
dst: GeneralReg,
src1: GeneralReg,
src2: GeneralReg,
);
fn call(buf: &mut Vec<'_, u8>, relocs: &mut Vec<'_, Relocation>, fn_name: String);
/// Jumps by an offset of offset bytes unconditionally.
@ -1600,6 +1621,66 @@ impl<
offset,
});
}
fn build_int_bitwise_and(
&mut self,
dst: &Symbol,
src1: &Symbol,
src2: &Symbol,
int_width: IntWidth,
) {
let buf = &mut self.buf;
match int_width {
IntWidth::U128 | IntWidth::I128 => todo!(),
_ => {
let dst_reg = self.storage_manager.claim_general_reg(buf, dst);
let src1_reg = self.storage_manager.load_to_general_reg(buf, src1);
let src2_reg = self.storage_manager.load_to_general_reg(buf, src2);
ASM::and_reg64_reg64_reg64(buf, dst_reg, src1_reg, src2_reg);
}
}
}
fn build_int_bitwise_or(
&mut self,
dst: &Symbol,
src1: &Symbol,
src2: &Symbol,
int_width: IntWidth,
) {
let buf = &mut self.buf;
match int_width {
IntWidth::U128 | IntWidth::I128 => todo!(),
_ => {
let dst_reg = self.storage_manager.claim_general_reg(buf, dst);
let src1_reg = self.storage_manager.load_to_general_reg(buf, src1);
let src2_reg = self.storage_manager.load_to_general_reg(buf, src2);
ASM::or_reg64_reg64_reg64(buf, dst_reg, src1_reg, src2_reg);
}
}
}
fn build_int_bitwise_xor(
&mut self,
dst: &Symbol,
src1: &Symbol,
src2: &Symbol,
int_width: IntWidth,
) {
let buf = &mut self.buf;
match int_width {
IntWidth::U128 | IntWidth::I128 => todo!(),
_ => {
let dst_reg = self.storage_manager.claim_general_reg(buf, dst);
let src1_reg = self.storage_manager.load_to_general_reg(buf, src1);
let src2_reg = self.storage_manager.load_to_general_reg(buf, src2);
ASM::xor_reg64_reg64_reg64(buf, dst_reg, src1_reg, src2_reg);
}
}
}
}
/// This impl block is for ir related instructions that need backend specific information.

View file

@ -916,6 +916,22 @@ fn x86_64_generic_cleanup_stack<'a>(
X86_64Assembler::pop_reg64(buf, X86_64GeneralReg::RBP);
}
type Reg64 = X86_64GeneralReg;
fn binop_move_src_to_dst_reg64<F>(buf: &mut Vec<'_, u8>, f: F, dst: Reg64, src1: Reg64, src2: Reg64)
where
F: FnOnce(&mut Vec<'_, u8>, X86_64GeneralReg, X86_64GeneralReg),
{
if dst == src1 {
f(buf, dst, src2);
} else if dst == src2 {
f(buf, dst, src1);
} else {
mov_reg64_reg64(buf, dst, src1);
f(buf, dst, src2);
}
}
impl Assembler<X86_64GeneralReg, X86_64FloatReg> for X86_64Assembler {
// These functions should map to the raw assembly functions below.
// In some cases, that means you can just directly call one of the direct assembly functions.
@ -954,22 +970,12 @@ impl Assembler<X86_64GeneralReg, X86_64FloatReg> for X86_64Assembler {
mov_reg64_reg64(buf, dst, src1);
add_reg64_imm32(buf, dst, imm32);
}
#[inline(always)]
fn add_reg64_reg64_reg64(
buf: &mut Vec<'_, u8>,
dst: X86_64GeneralReg,
src1: X86_64GeneralReg,
src2: X86_64GeneralReg,
) {
if dst == src1 {
add_reg64_reg64(buf, dst, src2);
} else if dst == src2 {
add_reg64_reg64(buf, dst, src1);
} else {
mov_reg64_reg64(buf, dst, src1);
add_reg64_reg64(buf, dst, src2);
}
fn add_reg64_reg64_reg64(buf: &mut Vec<'_, u8>, dst: Reg64, src1: Reg64, src2: Reg64) {
binop_move_src_to_dst_reg64(buf, add_reg64_reg64, dst, src1, src2)
}
#[inline(always)]
fn add_freg32_freg32_freg32(
buf: &mut Vec<'_, u8>,
@ -1253,31 +1259,20 @@ impl Assembler<X86_64GeneralReg, X86_64FloatReg> for X86_64Assembler {
#[inline(always)]
fn movsx_reg64_base32(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg, offset: i32, size: u8) {
debug_assert!(size <= 8);
if size == 8 {
Self::mov_reg64_base32(buf, dst, offset);
} else if size == 4 {
todo!("sign extending 4 byte values");
} else if size == 2 {
todo!("sign extending 2 byte values");
} else if size == 1 {
todo!("sign extending 1 byte values");
} else {
internal_error!("Invalid size for sign extension: {}", size);
match size {
8 => Self::mov_reg64_base32(buf, dst, offset),
4 | 2 | 1 => todo!("sign extending {size} byte values"),
_ => internal_error!("Invalid size for sign extension: {size}"),
}
}
#[inline(always)]
fn movzx_reg64_base32(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg, offset: i32, size: u8) {
debug_assert!(size <= 8);
if size == 8 {
Self::mov_reg64_base32(buf, dst, offset);
} else if size == 4 {
todo!("zero extending 4 byte values");
} else if size == 2 {
todo!("zero extending 2 byte values");
} else if size == 1 {
movzx_reg64_base8_offset32(buf, dst, X86_64GeneralReg::RBP, offset);
} else {
internal_error!("Invalid size for zero extension: {}", size);
match size {
8 => Self::mov_reg64_base32(buf, dst, offset),
4 | 2 => todo!("zero extending {size} byte values"),
1 => movzx_reg64_base8_offset32(buf, dst, X86_64GeneralReg::RBP, offset),
_ => internal_error!("Invalid size for zero extension: {size}"),
}
}
@ -1408,6 +1403,18 @@ impl Assembler<X86_64GeneralReg, X86_64FloatReg> for X86_64Assembler {
fn set_if_overflow(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg) {
seto_reg64(buf, dst);
}
fn and_reg64_reg64_reg64(buf: &mut Vec<'_, u8>, dst: Reg64, src1: Reg64, src2: Reg64) {
binop_move_src_to_dst_reg64(buf, and_reg64_reg64, dst, src1, src2)
}
fn or_reg64_reg64_reg64(buf: &mut Vec<'_, u8>, dst: Reg64, src1: Reg64, src2: Reg64) {
binop_move_src_to_dst_reg64(buf, or_reg64_reg64, dst, src1, src2)
}
fn xor_reg64_reg64_reg64(buf: &mut Vec<'_, u8>, dst: Reg64, src1: Reg64, src2: Reg64) {
binop_move_src_to_dst_reg64(buf, xor_reg64_reg64, dst, src1, src2)
}
}
impl X86_64Assembler {
@ -1511,6 +1518,27 @@ fn add_reg64_reg64(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg, src: X86_64Gene
binop_reg64_reg64(0x01, buf, dst, src);
}
/// `AND r/m64,r64` -> Bitwise logical and r64 to r/m64.
#[inline(always)]
fn and_reg64_reg64(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg, src: X86_64GeneralReg) {
// NOTE: src and dst are flipped by design
binop_reg64_reg64(0x23, buf, src, dst);
}
/// `OR r/m64,r64` -> Bitwise logical or r64 to r/m64.
#[inline(always)]
fn or_reg64_reg64(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg, src: X86_64GeneralReg) {
// NOTE: src and dst are flipped by design
binop_reg64_reg64(0x0B, buf, src, dst);
}
/// `XOR r/m64,r64` -> Bitwise logical exclusive or r64 to r/m64.
#[inline(always)]
fn xor_reg64_reg64(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg, src: X86_64GeneralReg) {
// NOTE: src and dst are flipped by design
binop_reg64_reg64(0x33, buf, src, dst);
}
/// `ADDSD xmm1,xmm2/m64` -> Add the low double-precision floating-point value from xmm2/mem to xmm1 and store the result in xmm1.
#[inline(always)]
fn addsd_freg64_freg64(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64FloatReg) {
@ -2189,13 +2217,6 @@ fn push_reg64(buf: &mut Vec<'_, u8>, reg: X86_64GeneralReg) {
}
}
/// `XOR r/m64,r64` -> Xor r64 to r/m64.
#[inline(always)]
#[allow(dead_code)]
fn xor_reg64_reg64(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg, src: X86_64GeneralReg) {
binop_reg64_reg64(0x31, buf, dst, src);
}
// When writing tests, it is a good idea to test both a number and unnumbered register.
// This is because R8-R15 often have special instruction prefixes.
#[cfg(test)]
@ -2341,11 +2362,31 @@ mod tests {
);
}
#[test]
fn test_and_reg64_reg64() {
disassembler_test!(
and_reg64_reg64,
|reg1, reg2| format!("and {reg1}, {reg2}"),
ALL_GENERAL_REGS,
ALL_GENERAL_REGS
);
}
#[test]
fn test_or_reg64_reg64() {
disassembler_test!(
or_reg64_reg64,
|reg1, reg2| format!("or {reg1}, {reg2}"),
ALL_GENERAL_REGS,
ALL_GENERAL_REGS
);
}
#[test]
fn test_xor_reg64_reg64() {
disassembler_test!(
xor_reg64_reg64,
|reg1, reg2| format!("xor {}, {}", reg1, reg2),
|reg1, reg2| format!("xor {reg1}, {reg2}"),
ALL_GENERAL_REGS,
ALL_GENERAL_REGS
);

View file

@ -499,6 +499,27 @@ trait Backend<'a> {
);
self.build_num_sub(sym, &args[0], &args[1], ret_layout)
}
LowLevel::NumBitwiseAnd => {
if let Layout::Builtin(Builtin::Int(int_width)) = ret_layout {
self.build_int_bitwise_and(sym, &args[0], &args[1], *int_width)
} else {
internal_error!("bitwise and on a non-integer")
}
}
LowLevel::NumBitwiseOr => {
if let Layout::Builtin(Builtin::Int(int_width)) = ret_layout {
self.build_int_bitwise_or(sym, &args[0], &args[1], *int_width)
} else {
internal_error!("bitwise or on a non-integer")
}
}
LowLevel::NumBitwiseXor => {
if let Layout::Builtin(Builtin::Int(int_width)) = ret_layout {
self.build_int_bitwise_xor(sym, &args[0], &args[1], *int_width)
} else {
internal_error!("bitwise xor on a non-integer")
}
}
LowLevel::Eq => {
debug_assert_eq!(2, args.len(), "Eq: expected to have exactly two argument");
debug_assert_eq!(
@ -750,6 +771,33 @@ trait Backend<'a> {
/// build_num_sub stores the `src1 - src2` difference into dst.
fn build_num_sub(&mut self, dst: &Symbol, src1: &Symbol, src2: &Symbol, layout: &Layout<'a>);
/// stores the `src1 & src2` into dst.
fn build_int_bitwise_and(
&mut self,
dst: &Symbol,
src1: &Symbol,
src2: &Symbol,
int_width: IntWidth,
);
/// stores the `src1 | src2` into dst.
fn build_int_bitwise_or(
&mut self,
dst: &Symbol,
src1: &Symbol,
src2: &Symbol,
int_width: IntWidth,
);
/// stores the `src1 ^ src2` into dst.
fn build_int_bitwise_xor(
&mut self,
dst: &Symbol,
src1: &Symbol,
src2: &Symbol,
int_width: IntWidth,
);
/// build_eq stores the result of `src1 == src2` into dst.
fn build_eq(&mut self, dst: &Symbol, src1: &Symbol, src2: &Symbol, arg_layout: &Layout<'a>);

View file

@ -8,7 +8,6 @@ use crate::llvm::build_list::{
list_prepend, list_replace_unsafe, list_reserve, list_sort_with, list_sublist, list_swap,
list_symbol_to_c_abi, list_with_capacity, pass_update_mode,
};
use crate::llvm::build_str::dec_to_str;
use crate::llvm::compare::{generic_eq, generic_neq};
use crate::llvm::convert::{
self, argument_type_from_layout, basic_type_from_builtin, basic_type_from_layout, zig_str_type,
@ -65,7 +64,7 @@ use std::convert::TryInto;
use std::path::Path;
use target_lexicon::{Architecture, OperatingSystem, Triple};
use super::convert::{zig_with_overflow_roc_dec, RocUnion};
use super::convert::{zig_dec_type, zig_with_overflow_roc_dec, RocUnion};
#[inline(always)]
fn print_fn_verification_output() -> bool {
@ -5821,24 +5820,60 @@ fn run_low_level<'a, 'ctx, 'env>(
// Str.getScalarUnsafe : Str, Nat -> { bytesParsed : Nat, scalar : U32 }
debug_assert_eq!(args.len(), 2);
use roc_target::OperatingSystem::*;
let string = load_symbol(scope, &args[0]);
let index = load_symbol(scope, &args[1]);
let result = call_str_bitcode_fn(
env,
&[string],
&[index],
BitcodeReturns::Basic,
bitcode::STR_GET_SCALAR_UNSAFE,
);
match env.target_info.operating_system {
Windows => {
// we have to go digging to find the return type
let function = env
.module
.get_function(bitcode::STR_GET_SCALAR_UNSAFE)
.unwrap();
// on 32-bit platforms, zig bitpacks the struct
match env.target_info.ptr_width() {
PtrWidth::Bytes8 => result,
PtrWidth::Bytes4 => {
let to = basic_type_from_layout(env, layout);
complex_bitcast_check_size(env, result, to, "to_roc_record")
let return_type = function.get_type().get_param_types()[0]
.into_pointer_type()
.get_element_type()
.into_struct_type();
let result = env.builder.build_alloca(return_type, "result");
call_void_bitcode_fn(
env,
&[result.into(), string, index],
bitcode::STR_GET_SCALAR_UNSAFE,
);
let return_type = basic_type_from_layout(env, layout);
let cast_result = env.builder.build_pointer_cast(
result,
return_type.ptr_type(AddressSpace::Generic),
"cast",
);
env.builder.build_load(cast_result, "load_result")
}
Unix => {
let result = call_str_bitcode_fn(
env,
&[string],
&[index],
BitcodeReturns::Basic,
bitcode::STR_GET_SCALAR_UNSAFE,
);
// on 32-bit platforms, zig bitpacks the struct
match env.target_info.ptr_width() {
PtrWidth::Bytes8 => result,
PtrWidth::Bytes4 => {
let to = basic_type_from_layout(env, layout);
complex_bitcast_check_size(env, result, to, "to_roc_record")
}
}
}
Wasi => unimplemented!(),
}
}
StrCountUtf8Bytes => {
@ -7323,39 +7358,123 @@ fn build_float_binop<'a, 'ctx, 'env>(
}
}
fn dec_split_into_words<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
value: IntValue<'ctx>,
) -> (IntValue<'ctx>, IntValue<'ctx>) {
let int_64 = env.context.i128_type().const_int(64, false);
let int_64_type = env.context.i64_type();
let left_bits_i128 = env
.builder
.build_right_shift(value, int_64, false, "left_bits_i128");
(
env.builder.build_int_cast(value, int_64_type, ""),
env.builder.build_int_cast(left_bits_i128, int_64_type, ""),
)
}
fn dec_alloca<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
value: IntValue<'ctx>,
) -> PointerValue<'ctx> {
let dec_type = zig_dec_type(env);
let alloca = env.builder.build_alloca(dec_type, "dec_alloca");
let instruction = alloca.as_instruction_value().unwrap();
instruction.set_alignment(16).unwrap();
let ptr = env.builder.build_pointer_cast(
alloca,
value.get_type().ptr_type(AddressSpace::Generic),
"cast_to_i128_ptr",
);
env.builder.build_store(ptr, value);
alloca
}
fn dec_to_str<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
dec: BasicValueEnum<'ctx>,
) -> BasicValueEnum<'ctx> {
use roc_target::OperatingSystem::*;
let dec = dec.into_int_value();
match env.target_info.operating_system {
Windows => {
//
call_str_bitcode_fn(
env,
&[],
&[dec_alloca(env, dec).into()],
BitcodeReturns::Str,
bitcode::DEC_TO_STR,
)
}
Unix => {
let (low, high) = dec_split_into_words(env, dec);
call_str_bitcode_fn(
env,
&[],
&[low.into(), high.into()],
BitcodeReturns::Str,
bitcode::DEC_TO_STR,
)
}
Wasi => unimplemented!(),
}
}
fn dec_binop_with_overflow<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
fn_name: &str,
lhs: BasicValueEnum<'ctx>,
rhs: BasicValueEnum<'ctx>,
) -> StructValue<'ctx> {
use roc_target::OperatingSystem::*;
let lhs = lhs.into_int_value();
let rhs = rhs.into_int_value();
let return_type = zig_with_overflow_roc_dec(env);
let return_alloca = env.builder.build_alloca(return_type, "return_alloca");
let int_64 = env.context.i128_type().const_int(64, false);
let int_64_type = env.context.i64_type();
match env.target_info.operating_system {
Windows => {
call_void_bitcode_fn(
env,
&[
return_alloca.into(),
dec_alloca(env, lhs).into(),
dec_alloca(env, rhs).into(),
],
fn_name,
);
}
Unix => {
let (lhs_low, lhs_high) = dec_split_into_words(env, lhs);
let (rhs_low, rhs_high) = dec_split_into_words(env, rhs);
let lhs1 = env
.builder
.build_right_shift(lhs, int_64, false, "lhs_left_bits");
let rhs1 = env
.builder
.build_right_shift(rhs, int_64, false, "rhs_left_bits");
call_void_bitcode_fn(
env,
&[
return_alloca.into(),
env.builder.build_int_cast(lhs, int_64_type, "").into(),
env.builder.build_int_cast(lhs1, int_64_type, "").into(),
env.builder.build_int_cast(rhs, int_64_type, "").into(),
env.builder.build_int_cast(rhs1, int_64_type, "").into(),
],
fn_name,
);
call_void_bitcode_fn(
env,
&[
return_alloca.into(),
lhs_low.into(),
lhs_high.into(),
rhs_low.into(),
rhs_high.into(),
],
fn_name,
);
}
Wasi => unimplemented!(),
}
env.builder
.build_load(return_alloca, "load_dec")
@ -7368,29 +7487,37 @@ pub fn dec_binop_with_unchecked<'a, 'ctx, 'env>(
lhs: BasicValueEnum<'ctx>,
rhs: BasicValueEnum<'ctx>,
) -> BasicValueEnum<'ctx> {
use roc_target::OperatingSystem::*;
let lhs = lhs.into_int_value();
let rhs = rhs.into_int_value();
let int_64 = env.context.i128_type().const_int(64, false);
let int_64_type = env.context.i64_type();
match env.target_info.operating_system {
Windows => {
// windows is much nicer for us here
call_bitcode_fn(
env,
&[dec_alloca(env, lhs).into(), dec_alloca(env, rhs).into()],
fn_name,
)
}
Unix => {
let (lhs_low, lhs_high) = dec_split_into_words(env, lhs);
let (rhs_low, rhs_high) = dec_split_into_words(env, rhs);
let lhs1 = env
.builder
.build_right_shift(lhs, int_64, false, "lhs_left_bits");
let rhs1 = env
.builder
.build_right_shift(rhs, int_64, false, "rhs_left_bits");
call_bitcode_fn(
env,
&[
env.builder.build_int_cast(lhs, int_64_type, "").into(),
env.builder.build_int_cast(lhs1, int_64_type, "").into(),
env.builder.build_int_cast(rhs, int_64_type, "").into(),
env.builder.build_int_cast(rhs1, int_64_type, "").into(),
],
fn_name,
)
call_bitcode_fn(
env,
&[
lhs_low.into(),
lhs_high.into(),
rhs_low.into(),
rhs_high.into(),
],
fn_name,
)
}
Wasi => unimplemented!(),
}
}
fn build_dec_binop<'a, 'ctx, 'env>(

View file

@ -46,30 +46,6 @@ pub(crate) fn decode_from_utf8_result<'a, 'ctx, 'env>(
}
/// Dec.toStr : Dec -> Str
pub(crate) fn dec_to_str<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
dec: BasicValueEnum<'ctx>,
) -> BasicValueEnum<'ctx> {
let dec = dec.into_int_value();
let int_64 = env.context.i128_type().const_int(64, false);
let int_64_type = env.context.i64_type();
let dec_right_shift = env
.builder
.build_right_shift(dec, int_64, false, "dec_left_bits");
let right_bits = env.builder.build_int_cast(dec, int_64_type, "");
let left_bits = env.builder.build_int_cast(dec_right_shift, int_64_type, "");
call_str_bitcode_fn(
env,
&[],
&[right_bits.into(), left_bits.into()],
BitcodeReturns::Str,
bitcode::DEC_TO_STR,
)
}
/// Str.equal : Str, Str -> Bool
pub(crate) fn str_equal<'a, 'ctx, 'env>(

View file

@ -420,6 +420,10 @@ pub fn zig_str_type<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> StructType<'ct
env.module.get_struct_type("str.RocStr").unwrap()
}
pub fn zig_dec_type<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> StructType<'ctx> {
env.module.get_struct_type("dec.RocDec").unwrap()
}
pub fn zig_has_tag_id_type<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> StructType<'ctx> {
let u8_ptr_t = env.context.i8_type().ptr_type(AddressSpace::Generic);

View file

@ -1299,7 +1299,7 @@ fn tan() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn bitwise_and() {
assert_evals_to!("Num.bitwiseAnd 20 20", 20, i64);
assert_evals_to!("Num.bitwiseAnd 25 10", 8, i64);
@ -1307,7 +1307,7 @@ fn bitwise_and() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn bitwise_xor() {
assert_evals_to!("Num.bitwiseXor 20 20", 0, i64);
assert_evals_to!("Num.bitwiseXor 15 14", 1, i64);
@ -1316,7 +1316,7 @@ fn bitwise_xor() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn bitwise_or() {
assert_evals_to!("Num.bitwiseOr 1 1", 1, i64);
assert_evals_to!("Num.bitwiseOr 1 2", 3, i64);

View file

@ -536,7 +536,7 @@ fn optional_field_let_no_use_default_nested() {
{ x ? 10, y } = r
x + y
f { x: 4, y: 9 }
f { y: 9, x: 4 }
"#
),
13,

View file

@ -598,6 +598,14 @@ macro_rules! assert_llvm_evals_to {
};
}
// windows testing code
// let mut target = target_lexicon::Triple::host();
//
// target.operating_system = target_lexicon::OperatingSystem::Windows;
//
// let (_main_fn_name, _delayed_errors, _module) =
// $crate::helpers::llvm::create_llvm_module(&arena, $src, config, &context, &target);
#[allow(unused_macros)]
macro_rules! assert_evals_to {
($src:expr, $expected:expr, $ty:ty) => {{

View file

@ -40,7 +40,7 @@ page_size = "0.4.2"
winit = "0.26.0"
wgpu = "0.12.0"
wgpu_glyph = "0.16.0"
glyph_brush = "0.7.2"
glyph_brush = "0.7.5"
log = "0.4.14"
env_logger = "0.9.0"
futures = "0.3.24"

View file

@ -313,6 +313,8 @@ e.g. you have a test `calculate_sum_test` that only uses the function `add`, whe
* Plugin to translate linux commands like curl to Roc code
* Plugin to view diff between two texts
* Plugin to present codebase to new developer or walk co-worker through a problem. Records sequence of filenames and line numbers.
* A Logbook plugin. I've found that writing down steps and thoughts when you're implementing or debugging something can be really useful for later.
If we make an integrated terminal, we can automatically add executed commands to this logbook. This plugin could have a publish button so you can produce useful "blogs" for others with minimal effort.
### Inspiration

View file

@ -1,5 +1,5 @@
app "rocLovesC"
packages { pf: "main.roc" }
packages { pf: "c-platform/main.roc" }
imports []
provides [main] to pf

View file

@ -1,5 +1,5 @@
app "rocLovesRust"
packages { pf: "main.roc" }
packages { pf: "rust-platform/main.roc" }
imports []
provides [main] to pf

View file

@ -1,5 +1,5 @@
app "rocLovesSwift"
packages { pf: "main.roc" }
packages { pf: "swift-platform/main.roc" }
imports []
provides [main] to pf

View file

@ -1,5 +1,5 @@
app "rocLovesWebAssembly"
packages { pf: "main.roc" }
packages { pf: "web-assembly-platform/main.roc" }
imports []
provides [main] to pf

View file

@ -1,5 +1,5 @@
app "rocLovesZig"
packages { pf: "main.roc" }
packages { pf: "zig-platform/main.roc" }
imports []
provides [main] to pf

View file

@ -3,8 +3,8 @@
To run this website, first compile either of these identical apps:
```bash
# Option A: Compile examples/platform-switching/web-assembly-platform/rocLovesWebAssembly.roc
cargo run -- build --target=wasm32 examples/platform-switching/web-assembly-platform/rocLovesWebAssembly.roc
# Option A: Compile examples/platform-switching/rocLovesWebAssembly.roc
cargo run -- build --target=wasm32 examples/platform-switching/rocLovesWebAssembly.roc
# Option B: Compile examples/platform-switching/main.roc with `pf: "web-assembly-platform/main.roc"` and move the result
cargo run -- build --target=wasm32 examples/platform-switching/main.roc

View file

@ -38,9 +38,9 @@
```sh
# Note: If you installed Rust in this terminal session, you'll need to open a new one first!
./roc examples/platform-switching/rust-platform/rocLovesRust.roc
./roc examples/platform-switching/rocLovesRust.roc
./roc examples/platform-switching/zig-platform/rocLovesZig.roc
./roc examples/platform-switching/rocLovesZig.roc
./roc examples/platform-switching/c-platform/rocLovesC.roc
./roc examples/platform-switching/rocLovesC.roc
```

View file

@ -34,9 +34,9 @@
```sh
# Note: If you installed rust in this terminal session, you'll need to open a new one first!
./roc examples/platform-switching/rust-platform/rocLovesRust.roc
./roc examples/platform-switching/rocLovesRust.roc
./roc examples/platform-switching/zig-platform/rocLovesZig.roc
./roc examples/platform-switching/rocLovesZig.roc
./roc examples/platform-switching/c-platform/rocLovesC.roc
./roc examples/platform-switching/rocLovesC.roc
```

View file

@ -34,9 +34,9 @@
```sh
# Note: If you installed rust in this terminal session, you'll need to open a new one first!
./roc examples/platform-switching/rust-platform/rocLovesRust.roc
./roc examples/platform-switching/rocLovesRust.roc
./roc examples/platform-switching/zig-platform/rocLovesZig.roc
./roc examples/platform-switching/rocLovesZig.roc
./roc examples/platform-switching/c-platform/rocLovesC.roc
./roc examples/platform-switching/rocLovesC.roc
```

View file

@ -7,11 +7,11 @@
1. Run examples:
```sh
cargo run examples/platform-switching/rust-platform/rocLovesRust.roc
cargo run examples/platform-switching/rocLovesRust.roc
# This requires installing the Zig compiler, too.
cargo run examples/platform-switching/zig-platform/rocLovesZig.roc
cargo run examples/platform-switching/rocLovesZig.roc
# This requires installing the `clang` C compiler, too.
cargo run examples/platform-switching/c-platform/rocLovesC.roc
cargo run examples/platform-switching/rocLovesC.roc
```

View file

@ -1,2 +1,3 @@
[files]
extend-exclude = ["crates/vendor/", "examples/static-site-gen/input/"]