Skip to content

Commit 29bb01e

Browse files
authored
Move rewrite event emission (#877)
This PR is a reworking of the changes initially made by @gtrepta in #862, to include the cleaned-up proof infrastructure in #876. The changes here are pretty minimal; we need to split the "rewrite" event into a pre and post part so that the arity / ordinal and resulting configuration can be emitted separately. Other than that, it's just a question of reusing some of the boilerplate in #862 that assembles the substitution information in the event. ~~There is one major issue with the change here, which @gtrepta also identified in his initial version of this change. We can only emit resulting configurations for top-level rewrites (that is, we cannot print the resulting _term_ from calling a function). Doing so breaks TCO for the generated interpreter badly enough that some backend tests crash; I have not dug into this in detail as I'm reasonably sure that's the reason things break.~~ * @nishantjr confirms that we can safely omit the result of function calls from the trace. ## Outstanding Issues I am working my way through the example below to ensure that the format being generated is precisely what we expect to see from the documentation. The issues I have identified are as follows: * [x] Duplicated KORE comments below are caused by function events not being bracketed as rewrites and hooks are; we need to pull the header out to the front, allocate args, then do the existing event. * [x] We need to handle binary KORE delimitation more consistently; at the moment it's wavy whether we emit header / footer or not. Should probably standardise on using the 1.2.0 self-limiting format. ## Worked Example Similarly to #862, I have generated a trace for the program `foo(0)` in the following definition: ```k module TEST imports INT syntax Int ::= "inc" Int [function, total] rule inc I => I +Int 1 syntax Foo ::= foo(Int) | bar(Int) rule foo(I) => bar(inc I) endmodule ``` The trace looks like: ``` hook: MAP.element function: Lbl'UndsPipe'-'-GT-Unds'{} (0) arg: kore[60] arg: kore[109] hook result: kore[207] hook: MAP.unit function: Lbl'Stop'Map{} (0) hook result: kore[51] hook: MAP.concat function: Lbl'Unds'Map'Unds'{} (0) arg: kore[51] arg: kore[207] hook result: kore[207] function: LblinitGeneratedTopCell{} (0) arg: kore[207] rule: 162 1 VarInit = kore[207] function: LblinitKCell{} (0:0) arg: kore[207] rule: 163 1 VarInit = kore[207] function: Lblproject'Coln'KItem{} (0:0:0) hook: MAP.lookup function: LblMap'Coln'lookup{} (0:0:0:0:0) arg: kore[207] arg: kore[60] hook result: kore[109] arg: kore[129] rule: 207 1 VarK = kore[109] function: LblinitGeneratedCounterCell{} (0:1) rule: 161 0 config: kore[237] rule: 126 3 Var'Unds'DotVar0 = kore[61] VarI = kore[50] Var'Unds'DotVar1 = kore[10] function: Lblinc'UndsUnds'TEST'Unds'Int'Unds'Int{} (0:0:0:0:0:0) arg: kore[50] rule: 160 1 VarI = kore[50] hook: INT.add function: Lbl'UndsPlus'Int'Unds'{} (0) arg: kore[50] arg: kore[50] hook result: kore[50] config: kore[237] ``` And was generated with the following (really horrible, quick and dirty) Python recogniser: <details><summary>Recogniser</summary> ```python #!/usr/bin/env python3 import sys import struct def load(fn): with open(fn, 'rb') as f: return f.read() def word(byte): return byte * 8 class Parser: def __init__(self, data): self.input = data self.depth = 0 def print(self, *args): print(f'{" " * self.depth}', end='') print(*args) def peek(self, cst): return self.input[:len(cst)] == cst def eof(self): return len(self.input) == 0 def skip(self, n): self.input = self.input[n:] def read_constant(self, cst): if self.input[:len(cst)] != cst: print(self.input[:40]) assert False self.input = self.input[len(cst):] def read_word(self, byte): assert len(byte) == 1 self.read_constant(byte * 8) def read_until(self, constant): index = self.input.find(constant) ret = self.input[:index] self.input = self.input[index:] return ret def read_string(self): data = self.read_until(b'\x00') self.read_constant(b'\x00') return str(data, 'ascii') def read_uint64(self): index = 8 raw = self.input[:index] self.input = self.input[index:] little_endian_long_long = '<Q' return struct.unpack(little_endian_long_long, raw)[0] def read_arg(self): self.depth += 1 if self.peek(b'\x7F'): l = self.read_kore() self.print(f'arg: kore[{l}]') self.depth -= 1 return True elif self.peek(word(b'\xDD')): self.read_function() self.depth -= 1 return True elif self.peek(word(b'\xAA')): self.read_hook() self.depth -= 1 return True elif self.peek(word(b'\x11')) or self.peek(word(b'\xBB')): self.depth -= 1 return False else: self.read_rule() self.depth -= 1 return True def read_hook(self): self.read_word(b'\xAA') name = self.read_string() self.print(f'hook: {name}') while True: if not self.read_arg(): break self.read_word(b'\xBB') result = self.read_kore() self.print(f'hook result: kore[{result}]') def read_function(self): self.read_word(b'\xDD') label = self.read_string() loc = self.read_string() self.print(f'function: {label} ({loc})') while True: if not self.read_arg(): break self.read_word(b'\x11') def read_kore(self): self.read_constant(b'\x7FKORE') self.skip(6) pattern_len = self.read_uint64() self.skip(pattern_len) return pattern_len def read_rule(self): ordinal = self.read_uint64() arity = self.read_uint64() self.print(f'rule: {ordinal} {arity}') for i in range(arity): self.read_variable() def read_variable(self): name = self.read_string() kore_len = self.read_kore() self.print(f' {name} = kore[{kore_len}]') self.read_word(b'\xCC') def read_config(self): self.read_word(b'\xFF') l = self.read_kore() self.print(f'config: kore[{l}]') self.read_word(b'\xCC') def read_trace(self): while not self.eof(): if self.peek(word(b'\xAA')): self.read_hook() elif self.peek(word(b'\xDD')): self.read_function() elif self.peek(word(b'\xFF')): self.read_config() else: self.read_rule() if __name__ == '__main__': data = load(sys.argv[1]) parser = Parser(data) parser.read_trace() ``` </details> I have verified by hand that this recogniser accepts all the proof traces from the proof generation repo's master branch, if they are regenerated with the LLVM backend pointed at this PR (`kup install k --override llvm-backend hint_emitPlace_2`).
1 parent 0e24009 commit 29bb01e

File tree

8 files changed

+111
-48
lines changed

8 files changed

+111
-48
lines changed

Diff for: docs/proof-trace.md

+27-23
Original file line numberDiff line numberDiff line change
@@ -19,38 +19,42 @@ have rendered this unnecessary, but the change hasn't been implemented yet.
1919

2020
## Grammar
2121

22-
Here is a BNF styled description of the format
23-
22+
Here is a BNF styled description of the format:
2423
```
25-
delimited_serial_kore := 0xffffffffffffffff serialized_term 0xcccccccccccccccc
26-
27-
null_terminated_name := <c-style null terminated string>
28-
relative_position := [0-9]+(:[0-9]+)* 0x00
29-
30-
function_event := 0xdddddddddddddddd null_terminated_name relative_position
24+
proof_trace ::= event*
3125
32-
hook_event := 0xaaaaaaaaaaaaaaaa null_terminated_name hook_arg* 0xbbbbbbbbbbbbbbbb serialized_term
26+
event ::= hook
27+
| function
28+
| rule
29+
| config
3330
34-
hook_arg := serialized_term
35-
| function_event
36-
| hook_event
31+
argument ::= hook
32+
| function
33+
| rule
34+
| kore_term
3735
38-
variable := null_terminated_name serialized_term 0xcccccccccccccccc
36+
name ::= string
37+
location ::= string
38+
function ::= WORD(0xDD) name location arg* WORD(0x11)
3939
40-
rule_ordinal := <64-bit unsigned little endian integer>
41-
rule_arity := <64-bit unsigned little endian integer>
40+
hook ::= WORD(0xAA) name arg* WORD(0xBB) kore_term
4241
43-
rewrite_trace := rule_ordinal rule_arity variable* delimited_serial_kore
42+
ordinal ::= uint64
43+
arity ::= uint64
44+
variable ::= name kore_term WORD(0xCC)
45+
rule ::= ordinal arity variable*
4446
45-
initial_config := delimited_serial_kore
47+
config ::= WORD(0xFF) kore_term WORD(0xCC)
4648
47-
proof_trace := (function_event|hook_event)* initial_config (function_event|hook_event|rewrite_trace)*
49+
string ::= <c-style null terminated string>
50+
uint64 ::= <64-bit unsigned little endian integer>
4851
```
4952

5053
## Notes
5154

52-
- The `rule_arity` should be used to determine how many variable substitutions to read
53-
- The serialized term for a variable substitution does not begin with the sentinel delimiter.
54-
This is because the null terminated variable name can act as the sentinel.
55-
- The `function_event|hook_event`s at the beginning of the proof_trace are related to configuration initialization.
56-
- The `relative_position` is a null terminated string of positive integers separated by `:` (ie. `0:1:1`)
55+
- The `rule_arity` should be used to determine how many variable substitutions
56+
to read.
57+
- Events at the beginning of the trace (i.e. before the first `config` event)
58+
are related to configuration initialization.
59+
- The `relative_position` is a null terminated string of positive integers
60+
separated by `:` (ie. `0:1:1`)

Diff for: include/kllvm/codegen/ProofEvent.h

+10-3
Original file line numberDiff line numberDiff line change
@@ -102,16 +102,23 @@ class ProofEvent {
102102
llvm::Value *val, KORECompositeSort *sort,
103103
llvm::BasicBlock *current_block);
104104

105-
[[nodiscard]] llvm::BasicBlock *rewriteEvent(
106-
KOREAxiomDeclaration *axiom, llvm::Value *return_value, uint64_t arity,
105+
[[nodiscard]] llvm::BasicBlock *rewriteEvent_pre(
106+
KOREAxiomDeclaration *axiom, uint64_t arity,
107107
std::map<std::string, KOREVariablePattern *> vars,
108108
llvm::StringMap<llvm::Value *> const &subst,
109109
llvm::BasicBlock *current_block);
110110

111-
[[nodiscard]] llvm::BasicBlock *functionEvent(
111+
[[nodiscard]] llvm::BasicBlock *rewriteEvent_post(
112+
KOREAxiomDeclaration *axiom, llvm::Value *return_value,
113+
llvm::BasicBlock *current_block);
114+
115+
[[nodiscard]] llvm::BasicBlock *functionEvent_pre(
112116
llvm::BasicBlock *current_block, KORECompositePattern *pattern,
113117
std::string const &locationStack);
114118

119+
[[nodiscard]] llvm::BasicBlock *
120+
functionEvent_post(llvm::BasicBlock *current_block);
121+
115122
public:
116123
ProofEvent(KOREDefinition *Definition, llvm::Module *Module)
117124
: Definition(Definition)

Diff for: lib/codegen/CreateTerm.cpp

+7-6
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,10 @@ llvm::Value *CreateTerm::createHook(
669669
llvm::Value *CreateTerm::createFunctionCall(
670670
std::string name, KORECompositePattern *pattern, bool sret, bool tailcc,
671671
std::string locationStack) {
672+
auto event = ProofEvent(Definition, Module);
673+
674+
CurrentBlock = event.functionEvent_pre(CurrentBlock, pattern, locationStack);
675+
672676
std::vector<llvm::Value *> args;
673677
auto returnSort = dynamic_cast<KORECompositeSort *>(
674678
pattern->getConstructor()->getSort().get());
@@ -697,8 +701,7 @@ llvm::Value *CreateTerm::createFunctionCall(
697701
}
698702
}
699703

700-
auto event = ProofEvent(Definition, Module);
701-
CurrentBlock = event.functionEvent(CurrentBlock, pattern, locationStack);
704+
CurrentBlock = event.functionEvent_post(CurrentBlock);
702705

703706
return createFunctionCall(name, returnCat, args, sret, tailcc, locationStack);
704707
}
@@ -1069,10 +1072,8 @@ bool makeFunction(
10691072

10701073
auto CurrentBlock = creator.getCurrentBlock();
10711074
if (apply && bigStep) {
1072-
auto event = ProofEvent(definition, Module);
1073-
CurrentBlock = event.rewriteEvent(
1074-
axiom, retval, applyRule->arg_end() - applyRule->arg_begin(), vars,
1075-
subst, CurrentBlock);
1075+
CurrentBlock = ProofEvent(definition, Module)
1076+
.rewriteEvent_post(axiom, retval, CurrentBlock);
10761077
}
10771078

10781079
if (bigStep) {

Diff for: lib/codegen/Decision.cpp

+30-4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include "kllvm/codegen/CreateTerm.h"
44
#include "kllvm/codegen/Debug.h"
5+
#include "kllvm/codegen/ProofEvent.h"
56
#include "kllvm/codegen/Util.h"
67

78
#include <llvm/ADT/APInt.h>
@@ -549,6 +550,7 @@ void LeafNode::codegen(Decision *d) {
549550
setCompleted();
550551
return;
551552
}
553+
552554
std::vector<llvm::Value *> args;
553555
std::vector<llvm::Type *> types;
554556
for (auto arg : bindings) {
@@ -557,12 +559,36 @@ void LeafNode::codegen(Decision *d) {
557559
types.push_back(val->getType());
558560
}
559561
auto type = getParamType(d->Cat, d->Module);
560-
auto Call = llvm::CallInst::Create(
561-
getOrInsertFunction(
562-
d->Module, name, llvm::FunctionType::get(type, types, false)),
563-
args, "", d->CurrentBlock);
562+
563+
auto applyRule = getOrInsertFunction(
564+
d->Module, name, llvm::FunctionType::get(type, types, false));
565+
566+
// We are generating code for a function with name beginning apply_rule_\d+; to
567+
// retrieve the corresponding ordinal we drop the apply_rule_ prefix.
568+
auto ordinal = std::stoll(name.substr(11));
569+
auto arity = applyRule->arg_end() - applyRule->arg_begin();
570+
auto axiom = d->Definition->getAxiomByOrdinal(ordinal);
571+
572+
auto vars = std::map<std::string, KOREVariablePattern *>{};
573+
for (KOREPattern *lhs : axiom->getLeftHandSide()) {
574+
lhs->markVariables(vars);
575+
}
576+
axiom->getRightHandSide()->markVariables(vars);
577+
578+
auto subst = llvm::StringMap<llvm::Value *>{};
579+
auto i = 0;
580+
for (auto iter = vars.begin(); iter != vars.end(); ++iter, ++i) {
581+
subst[iter->first] = args[i];
582+
}
583+
584+
d->CurrentBlock
585+
= ProofEvent(d->Definition, d->Module)
586+
.rewriteEvent_pre(axiom, arity, vars, subst, d->CurrentBlock);
587+
588+
auto Call = llvm::CallInst::Create(applyRule, args, "", d->CurrentBlock);
564589
setDebugLoc(Call);
565590
Call->setCallingConv(llvm::CallingConv::Tail);
591+
566592
if (child == nullptr) {
567593
llvm::ReturnInst::Create(d->Ctx, Call, d->CurrentBlock);
568594
} else {

Diff for: lib/codegen/ProofEvent.cpp

+32-8
Original file line numberDiff line numberDiff line change
@@ -211,13 +211,13 @@ llvm::BasicBlock *ProofEvent::hookArg(
211211
* Rewrite Events
212212
*/
213213

214-
llvm::BasicBlock *ProofEvent::rewriteEvent(
215-
KOREAxiomDeclaration *axiom, llvm::Value *return_value, uint64_t arity,
214+
llvm::BasicBlock *ProofEvent::rewriteEvent_pre(
215+
KOREAxiomDeclaration *axiom, uint64_t arity,
216216
std::map<std::string, KOREVariablePattern *> vars,
217217
llvm::StringMap<llvm::Value *> const &subst,
218218
llvm::BasicBlock *current_block) {
219219
auto [true_block, merge_block, outputFile]
220-
= eventPrelude("rewrite", current_block);
220+
= eventPrelude("rewrite_pre", current_block);
221221

222222
emitWriteUInt64(outputFile, axiom->getOrdinal(), true_block);
223223
emitWriteUInt64(outputFile, arity, true_block);
@@ -233,9 +233,22 @@ llvm::BasicBlock *ProofEvent::rewriteEvent(
233233
emitWriteUInt64(outputFile, word(0xCC), true_block);
234234
}
235235

236-
emitWriteUInt64(outputFile, word(0xFF), true_block);
237-
emitSerializeConfiguration(outputFile, return_value, true_block);
238-
emitWriteUInt64(outputFile, word(0xCC), true_block);
236+
llvm::BranchInst::Create(merge_block, true_block);
237+
return merge_block;
238+
}
239+
240+
llvm::BasicBlock *ProofEvent::rewriteEvent_post(
241+
KOREAxiomDeclaration *axiom, llvm::Value *return_value,
242+
llvm::BasicBlock *current_block) {
243+
auto [true_block, merge_block, output_file]
244+
= eventPrelude("rewrite_post", current_block);
245+
246+
auto return_sort = std::dynamic_pointer_cast<KORECompositeSort>(
247+
axiom->getRightHandSide()->getSort());
248+
249+
emitWriteUInt64(output_file, word(0xFF), true_block);
250+
emitSerializeTerm(*return_sort, output_file, return_value, true_block);
251+
emitWriteUInt64(output_file, word(0xCC), true_block);
239252

240253
llvm::BranchInst::Create(merge_block, true_block);
241254
return merge_block;
@@ -245,11 +258,11 @@ llvm::BasicBlock *ProofEvent::rewriteEvent(
245258
* Function Events
246259
*/
247260

248-
llvm::BasicBlock *ProofEvent::functionEvent(
261+
llvm::BasicBlock *ProofEvent::functionEvent_pre(
249262
llvm::BasicBlock *current_block, KORECompositePattern *pattern,
250263
std::string const &locationStack) {
251264
auto [true_block, merge_block, outputFile]
252-
= eventPrelude("function", current_block);
265+
= eventPrelude("function_pre", current_block);
253266

254267
std::ostringstream symbolName;
255268
pattern->getConstructor()->print(symbolName);
@@ -262,4 +275,15 @@ llvm::BasicBlock *ProofEvent::functionEvent(
262275
return merge_block;
263276
}
264277

278+
llvm::BasicBlock *
279+
ProofEvent::functionEvent_post(llvm::BasicBlock *current_block) {
280+
auto [true_block, merge_block, outputFile]
281+
= eventPrelude("function_post", current_block);
282+
283+
emitWriteUInt64(outputFile, word(0x11), true_block);
284+
285+
llvm::BranchInst::Create(merge_block, true_block);
286+
return merge_block;
287+
}
288+
265289
} // namespace kllvm

Diff for: runtime/take_steps.ll

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ target triple = "@BACKEND_TARGET_TRIPLE@"
66

77
declare tailcc %block* @k_step(%block*)
88
declare tailcc %block** @stepAll(%block*, i64*)
9-
declare void @serializeConfigurationToFile(i8*, %block*)
9+
declare void @serializeConfigurationToFile(i8*, %block*, i1)
1010
declare void @writeUInt64ToFile(i8*, i64)
1111

1212
@proof_output = external global i1
@@ -51,7 +51,7 @@ define %block* @take_steps(i64 %depth, %block* %subject) {
5151
if:
5252
%output_file = load i8*, i8** @output_file
5353
call void @writeUInt64ToFile(i8* %output_file, i64 18446744073709551615)
54-
call void @serializeConfigurationToFile(i8* %output_file, %block* %subject)
54+
call void @serializeConfigurationToFile(i8* %output_file, %block* %subject, i1 1)
5555
call void @writeUInt64ToFile(i8* %output_file, i64 14757395258967641292)
5656
br label %merge
5757
merge:

Diff for: runtime/util/ConfigurationPrinter.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,7 @@ void printVariableToFile(const char *filename, const char *varname) {
382382
fprintf(file, "%s", varname);
383383
char n = 0;
384384
fwrite(&n, 1, 1, file);
385+
fflush(file);
385386

386387
fclose(file);
387388
}

Diff for: runtime/util/ConfigurationSerializer.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,7 @@ void serializeTermToFile(
431431
const char *filename, block *subject, const char *sort) {
432432
char *data;
433433
size_t size;
434-
serializeConfiguration(subject, sort, &data, &size, false);
434+
serializeConfiguration(subject, sort, &data, &size, true);
435435

436436
FILE *file = fopen(filename, "a");
437437
fwrite(data, 1, size, file);
@@ -444,7 +444,7 @@ void serializeRawTermToFile(
444444

445445
char *data;
446446
size_t size;
447-
serializeConfiguration(term, "SortKItem{}", &data, &size, false);
447+
serializeConfiguration(term, "SortKItem{}", &data, &size, true);
448448

449449
FILE *file = fopen(filename, "a");
450450
fwrite(data, 1, size, file);

0 commit comments

Comments
 (0)