Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Frontend Holes #107

Open
wants to merge 34 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
2d3e93d
Some holes in typing
stephenverderame Jul 21, 2024
556d1ef
Rebase and a little setup
stephenverderame Jul 23, 2024
91bb72e
Holes with reaching defs
stephenverderame Jul 28, 2024
64f1c15
Fix reaching defs
stephenverderame Jul 28, 2024
d710ac0
Don't give up on typing holes
stephenverderame Jul 29, 2024
91b1667
Hole in encode copy
stephenverderame Aug 3, 2024
94380f5
Expression holes that generate code
stephenverderame Aug 4, 2024
9a21ccc
Hole copy
stephenverderame Aug 5, 2024
ea7342b
Simple file check
stephenverderame Aug 16, 2024
d5baf0d
Holes across funclets
stephenverderame Aug 16, 2024
c7764bd
Better hole tests
stephenverderame Aug 18, 2024
b76d543
Temp fix to flags
stephenverderame Aug 23, 2024
14e42aa
Hole idea somewhat fleshed out, needs testing
stephenverderame Aug 26, 2024
9407c02
More holes, fix analysis
stephenverderame Sep 1, 2024
7319b9e
Pass reordering
stephenverderame Sep 1, 2024
c1cd234
Some more tests
stephenverderame Sep 2, 2024
39bdd31
More fixes for another example
stephenverderame Sep 5, 2024
fb27eea
Renaming to avoid conflicts
stephenverderame Sep 6, 2024
3067fb4
Active fences with holes
stephenverderame Sep 6, 2024
c5da583
More tests
stephenverderame Sep 7, 2024
ae48821
Silence some warnings, fix borrow mut error
stephenverderame Sep 7, 2024
a363cd6
Remove typing subtype restriction on readable members of a remote obj
stephenverderame Sep 8, 2024
7fa91c4
Better error messages, fix usability analysis
stephenverderame Sep 12, 2024
4553f24
Catch more errors
stephenverderame Sep 15, 2024
48dce03
Some cleanup
stephenverderame Sep 22, 2024
de9aeaa
Refactor to prep for init placement algorithm
stephenverderame Nov 3, 2024
473c68e
Start Init Algorithm Part 1
stephenverderame Nov 3, 2024
01aa0d0
Init Algorithm Phase 1 working
stephenverderame Nov 4, 2024
e29e86c
Minimize initializer sets
stephenverderame Nov 6, 2024
1b7a40a
Don't assume phi nodes have select quotients
stephenverderame Nov 10, 2024
a23703e
Initializer Placement Phase 2
stephenverderame Nov 18, 2024
7690c2c
Consider static defs in phase 2 init alg
stephenverderame Nov 19, 2024
63d7d78
Phase 2 Testing and Fixes
stephenverderame Nov 23, 2024
8407bb5
Meta Var Refactor
stephenverderame Nov 25, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

247 changes: 247 additions & 0 deletions caiman-test/fcheck.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
"""
This is similar to LLVM's FileCheck.

In a source file, place patterns between #CHECK and #END (multiline allowed).
The text between these directives will be searched for in the IR file. The search
keys must be found in the ir file in sequential order to be successful.

Except for the following notes, this pretty much matches what you'd expect from
FileCheck for the parts of FileCheck that this script supports.

Notes:
* spaces are important, but any amount of whitespace can match 1 or more whitespace
in the test file. Don't put a space/newline if a
space may not be there in the test file.
* `...` is the same as the regex (.|\n)*?, except that the match must contain
balanced braces
* you cannot use a variable in the same #CHECK #END section as the one in which
you defined it
* globals can be defined on the command line with the -d flag (Ex: `-d name=val`)
and can be used in your search key with the syntax `${name}`.

Supported Features:
* CHECK-NOT (partial, checks for no match after the last match only)
* CHECK-LABEL
* Regexes
* Substitution blocks
"""

import sys
import re
from typing import TextIO


def check_scope_str(s: str) -> None | str:
"""
Ensures that `s` is a string where all openining braces are correctly closed.
Returns an error string when `s` is invalid, otherwise `None` when `s` is valid.
"""
stack = []
for c in s:
if c == "{" or c == "[" or c == "(":
stack.append(c)

if c == "}" or c == "]" or c == ")":
try:
r = stack.pop()
except:
return (
f"Encountered closing brace: {c} without a matching opening"
)
if (
(c == "}" and r != "{")
or (c == "]" and r != "[")
or (c == ")" and r != "(")
):
return f"Mismatch closing brace {c} for {r}"
if len(stack) > 0:
return f"Missing closing braces for {stack}"
return None


# Global constants
vars = {}


def get_src_ir_file() -> tuple[str, TextIO]:
"""
Gets the name of the src file and the IO for the ir file to check.
Handles command line arguments and processes global constants in the
command line arguments specified with `-d name=val`
"""
# Is the next cmd line arg a definition
is_def = False
# List of arguments without global constants or other options
new_args = []
for cmd in sys.argv:
if is_def:
is_def = False
if not "=" in cmd:
print("An argument definition must follow -d", file=sys.stderr)
var = cmd.split("=")
vars[var[0]] = var[1]
elif cmd == "-d":
is_def = True
else:
new_args.append(cmd)

if not (len(new_args) == 2 or len(new_args) == 3):
print("Usage: fcheck <src_file> <ir_file>")
print(" fcheck <src_file>")
exit(1)

src_file = new_args[1]
f_out = open(new_args[2]) if len(new_args) == 3 else sys.stdin
return src_file, f_out


src_file, f_ir = get_src_ir_file()


def in_any_group(start_pos: int, end_pos: int, groups: list[dict]) -> bool:
"""
Returns `True` if `start_pos` and `end_pos` overlap with any group in `groups`
where a group is defined by a dict with a `start` and `end` key-value pair
"""
for group in groups:
if start_pos >= group["start"] and (
"end" not in group or end_pos < group["end"]
):
return True
return False


with open(src_file) as f_src:
s_in = f_src.read()
s_out = f_ir.read()
groups: list[dict] = []
last_src_pos = -1
groups.append({"start": 0})
names = {}

for match in re.finditer(
re.compile(r"#CHECK(-([a-zA-Z_-]+))?:\s*((.|\s)*?)#END", re.MULTILINE),
s_in,
):
txt = match.group(3).rstrip() # the text between the #CHECK and #END
opt = match.group(2) # any option applies to the check command
# the list of parts of the text between the #CHECK and #END
# made up of literal strings to match and regexes that are surrounded with `{{}}`
parts: list[str] = []
# the last index of the last part in the list
last_part_idx = 0
# any names defined with the syntax [[name:regex]]
defined_names = []
for regex_or_name in re.finditer(
re.compile(
r"{{(.*)}}|\[\[([a-zA-Z][a-zA-Z0-9_]*:)?(.*)\]\]|\${(.+)}"
),
txt,
):
parts.append(txt[last_part_idx : regex_or_name.start()])
last_part_idx = regex_or_name.end()
if regex_or_name.group(0).startswith("{"):
# we're a regex
parts.append(regex_or_name.group(0))
elif regex_or_name.group(0).startswith("${"):
# we're a global constant
var_name = regex_or_name.group(4)
assert var_name in vars, f"Undefined global constant {var_name}"
parts.append(vars[var_name])
else:
# we must be a variable use or decl
def_name = regex_or_name.group(2)
if def_name is not None and len(def_name) > 0:
# turn a var definition into a named group
parts.append(
r"{{"
+ f"(?P<{def_name[:-1]}>({regex_or_name.group(3)}))"
+ r"}}"
)
defined_names.append(def_name[:-1])
else:
# we're a use, replace with the literal string and append to parts
name = regex_or_name.group(3)
if not name in names:
print(
f"Undefined use of {name} Defined names: {names}",
file=sys.stderr,
)
exit(2)
parts.append(names.get(name))
# append leftovers
parts.append(txt[last_part_idx:])
dots = 0
for i in range(0, len(parts)):
part = parts[i]
if part.startswith("{{"):
# the part is a regex, trim the starting and ending brackets
assert part.endswith("}}")
part = part[2:-2]
else:
# the part is literal text, sanitize it
part = re.sub(re.compile(r"\+"), r"\\+", part)
part = re.sub(re.compile(r"\("), r"\\(", part)
part = re.sub(re.compile(r"\)"), r"\\)", part)
part = re.sub(re.compile(r"\*"), r"\\*", part)
part = re.sub(re.compile(r"\["), r"\\[", part)
part = re.sub(re.compile(r"\]"), r"\\]", part)
part = re.sub(re.compile(r"\""), r"\"", part)
part = re.sub(re.compile(r"\s+"), r"\\s+", part)
# convert ... into a named group so we can check the brackets
while "..." in part:
part = part.replace(
"...", f"(?P<_scope{dots}>(\\n|.)*?)", 1
)
dots += 1
parts[i] = part

regex = ""
for part in parts:
regex += part

# regex uses lookahead so it won't consume input and allow us to go through
# overlapping regexes
regex = f"(?=({regex}))"
success = opt == "NOT"
if opt == "LABEL":
groups[-1]["end"] = last_src_pos
last_src_pos = -1
for out_match in re.finditer(re.compile(regex), s_out):
# Allowing overlapping matches means that a match technically
# has 0 length. Thus we do the end calculation manually
match_end = out_match.start() + len(out_match.group(1))
if out_match.start() >= last_src_pos and not in_any_group(
out_match.start(), match_end, groups[:-1]
):
for name in defined_names:
# for any defined names in this pattern, add them to the
# variable environment
names[name] = out_match.group(name)
cont = False
for i in range(0, dots):
group = out_match.group(f"_scope{i}")
err = check_scope_str(group)
if err is not None:
# print(
# f"{err} in match\n{group}",
# file=sys.stderr,
# )
cont = True
if cont:
continue
if opt == "NOT":
# print(out_match.start(), file=sys.stderr)
success = False
else:
last_src_pos = match_end
success = True
if opt == "LABEL":
groups.append({"start": out_match.start()})
break

if not success:
st = "Found" if opt == "NOT" else "Failed to find"
print(f"{st}:\n {txt}", file=sys.stderr)
# print(f"\n\nWith regex:\n{regex}", file=sys.stderr)
exit(1)
1 change: 0 additions & 1 deletion caiman-test/high-level-caiman/flatten/flat_min2_test.cm
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ val min() -> i64 {
fn min_impl() -> i64
impls min, time, space
{
// TODO: we need to pass 1 thing for codegen to work
let a = 2;
let r = if 2 < 1 {
a
Expand Down
24 changes: 24 additions & 0 deletions caiman-test/high-level-caiman/holes/fib_hole_test.cm
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#version 0.1.0

tmln time(e: Event) -> Event { returns e }
sptl space(s: BufferSpace) -> BufferSpace { returns s }

val fib(a: i64) -> i64 {
returns 0 if a <= 0 else
1 if a == 1 else
fib(a - 1) + fib(a - 2)
}

fn fib_impl(a: i64) -> i64 impls fib, time, space {
if a <= 0 {
0
} else if ? == 1 {
1
} else {
let x = fib_impl(a - 1);
let y = fib_impl(a - 2);
? + ?
}
}

pipeline main { fib_impl }
32 changes: 32 additions & 0 deletions caiman-test/high-level-caiman/holes/fib_hole_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
struct Callbacks;

impl main::CpuFunctions for Callbacks {
fn _add_i64_i64(&self, _: &mut dyn caiman_rt::State, a: i64, b: i64) -> (i64,) {
(a + b,)
}
fn _eq_i64_i64(&self, _: &mut dyn caiman_rt::State, a: i64, b: i64) -> (i32,) {
(if a == b { 1 } else { 0 },)
}
fn _leq_i64_i64(&self, _: &mut dyn caiman_rt::State, a: i64, b: i64) -> (i32,) {
(if a <= b { 1 } else { 0 },)
}
fn _sub_i64_i64(&self, _: &mut dyn caiman_rt::State, a: i64, b: i64) -> (i64,) {
(a - b,)
}
}

#[test]
fn main() -> Result<(), String> {
let callbacks = Callbacks;
let mut wgpu_instance = crate::util::INSTANCE.lock().unwrap();
let mut root_state = wgpu_instance.create_root_state();
let mut join_stack_bytes = [0u8; 4096usize];
let mut join_stack = caiman_rt::JoinStack::new(&mut join_stack_bytes);
let instance = main::Instance::new(&mut root_state, &callbacks);
let mut result = instance.start(&mut join_stack, 8);
while result.returned().is_none() {
let instance = result.prepare_next();
result = instance.resume_at__loop_impl(&mut join_stack);
}
crate::expect_returned!(21, result.returned().map(|x| x.0))
}
15 changes: 15 additions & 0 deletions caiman-test/high-level-caiman/holes/gpu_external.comp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#version 450

layout(set = 0, binding = 0) readonly buffer Input_0 {
int field_0;
} input_0;

layout(set = 0, binding = 1) buffer Output_0 {
int field_0;
} output_0;

layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
void main()
{
output_0.field_0 = input_0.field_0 + 1;
}
15 changes: 15 additions & 0 deletions caiman-test/high-level-caiman/holes/hlc_call_func_hole.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
struct Callbacks;

impl main::CpuFunctions for Callbacks {}

#[test]
fn main() -> Result<(), String> {
let callbacks = Callbacks;
let mut wgpu_instance = crate::util::INSTANCE.lock().unwrap();
let mut root_state = wgpu_instance.create_root_state();
let mut join_stack_bytes = [0u8; 4096usize];
let mut join_stack = caiman_rt::JoinStack::new(&mut join_stack_bytes);
let instance = main::Instance::new(&mut root_state, &callbacks);
let result = instance.start(&mut join_stack);
crate::expect_returned!(1, result.returned().map(|x| x.0))
}
27 changes: 27 additions & 0 deletions caiman-test/high-level-caiman/holes/hlc_call_func_hole_wip.cm
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#version 0.1.0

tmln time(e: Event) -> Event { returns e }
sptl space(s: BufferSpace) -> BufferSpace { returns s }

val main() -> o: i64 {
c :- 1
r :- id(c)
returns r
}

val id(a: i64) -> b: i64 {
returns a
}

fn main_func() -> i64 impls main, time, space {
let c = 1;
id_func(?)
}

fn id_func(a: i64 @ input(val.a)-usable) -> i64 @ node(val.b)-usable
impls id, time, space
{
a
}

pipeline main { main_func }
Loading