Skip to content

Commit

Permalink
finish t7
Browse files Browse the repository at this point in the history
  • Loading branch information
hj424 committed Dec 7, 2020
1 parent 28f5541 commit ec0ebb4
Show file tree
Hide file tree
Showing 9 changed files with 514 additions and 0 deletions.
38 changes: 38 additions & 0 deletions tasks/t7/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
Tracing JIT with Profile Guided AOT Setting
=========================================================

This Python based implementation supports the optimization based on the profiling file: <br />
As you can see from the tests folder, the example named "branch.bril" is the test source code. And the file named "branch_profile.json" is the profiling file which contains the branch instruction and the cooresponding first instructions of the branch destination block. <br/>
The example named "branch.bril" is manually written code, whereas the "branch_profile.json" is generated by a modified brili interpreter. <br/>


## Run the code
```javascript
// without tracing based JIT optimization
$ bril2json < tests/branch.bril | brili -p 3
// with tracing based JIT optimization
$ bril2json < tests/branch.bril | python tracing_JIT.py | brili -p 3
// with tracing based JIT optimization and DCE optimization
$ bril2json < tests/branch.bril | python tracing_JIT.py | brili -p 3
```

## Sample Outputs
```javascript
// without tracing based JIT optimization
$ bril2json < tests/branch.bril | brili -p 3

total_dyn_inst: 10

// with tracing based JIT optimization
$ bril2json < tests/branch.bril | python tracing_JIT.py | brili -p 3

total_dyn_inst: 12

// with tracing based JIT optimization and DCE optimization
$ bril2json < tests/branch.bril | python tracing_JIT.py | brili -p 3

total_dyn_inst: 10
```

We evaluate the optimization by leveraging the total number of dynamic instruction count offered by the "-p" flag. For this example, without JIT optimication ,the total cycle count for the input "3" is 10. Then with the JIT optimization, the total cycle count is even larger, i.e., 12. The reason is that we are adding three more instructions (speculate, guard, and commit) in the path and only delete one branch instruction. Thus the total cycle count is 12. <br/>
But if we apply other optimizations, like the DCE optimizaion from previous task, the dead code are eliminated by speculating the branch path. Thus, the total cycle count for the third experiment is 10. As for more complicated examples, this profiling based JIT optimization will save more cycles combined with other optimizing techniques like LVN, LICM, etc. <br/>
77 changes: 77 additions & 0 deletions tasks/t7/cfg.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
from collections import OrderedDict
from util import fresh
from form_blocks import TERMINATORS


def block_map(blocks):
"""Given a sequence of basic blocks, which are lists of instructions,
produce a `OrderedDict` mapping names to blocks.
The name of the block comes from the label it starts with, if any.
Anonymous blocks, which don't start with a label, get an
automatically generated name. Blocks in the mapping have their
labels removed.
"""
by_name = OrderedDict()

for block in blocks:
# Generate a name for the block.
if 'label' in block[0]:
# The block has a label. Remove the label but use it for the
# block's name.
name = block[0]['label']
block = block[1:]
else:
# Make up a new name for this anonymous block.
name = fresh('b', by_name)

# Add the block to the mapping.
by_name[name] = block

return by_name


def successors(instr):
"""Get the list of jump target labels for an instruction.
Raises a ValueError if the instruction is not a terminator (jump,
branch, or return).
"""
if instr['op'] in ('jmp', 'br'):
return instr['labels']
elif instr['op'] == 'ret':
return [] # No successors to an exit block.
else:
raise ValueError('{} is not a terminator'.format(instr['op']))


def add_terminators(blocks):
"""Given an ordered block map, modify the blocks to add terminators
to all blocks (avoiding "fall-through" control flow transfers).
"""
for i, block in enumerate(blocks.values()):
if not block:
dest = list(blocks.keys())[i + 1]
block.append({'op': 'jmp', 'labels': [dest]})
elif block[-1]['op'] not in TERMINATORS:
if i == len(blocks) - 1:
# In the last block, return.
block.append({'op': 'ret', 'args': []})
else:
# Otherwise, jump to the next block.
dest = list(blocks.keys())[i + 1]
block.append({'op': 'jmp', 'labels': [dest]})


def edges(blocks):
"""Given a block map containing blocks complete with terminators,
generate two mappings: predecessors and successors. Both map block
names to lists of block names.
"""
preds = {name: [] for name in blocks}
succs = {name: [] for name in blocks}
for name, block in blocks.items():
for succ in successors(block[-1]):
succs[name].append(succ)
preds[succ].append(name)
return preds, succs
71 changes: 71 additions & 0 deletions tasks/t7/form_blocks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
"""Create and print out the basic blocks in a Bril function.
"""

import json
import sys

# Instructions that terminate a basic block.
TERMINATORS = 'br', 'jmp', 'ret'


def form_blocks(instrs):
"""Given a list of Bril instructions, generate a sequence of
instruction lists representing the basic blocks in the program.
Every instruction in `instr` will show up in exactly one block. Jump
and branch instructions may only appear at the end of a block, and
control can transfer only to the top of a basic block---so labels
can only appear at the *start* of a basic block. Basic blocks may
not be empty.
"""

# Start with an empty block.
cur_block = []

for instr in instrs:
if 'op' in instr: # It's an instruction.
# Add the instruction to the currently-being-formed block.
cur_block.append(instr)

# If this is a terminator (branching instruction), it's the
# last instruction in the block. Finish this block and
# start a new one.
if instr['op'] in TERMINATORS:
yield cur_block
cur_block = []

else: # It's a label.
# End the block here (if it contains anything).
if cur_block:
yield cur_block

# Start a new block with the label.
cur_block = [instr]

# Produce the final block, if any.
if cur_block:
yield cur_block


def print_blocks(bril):
"""Given a Bril program, print out its basic blocks.
"""
import briltxt

func = bril['functions'][0] # We only process one function.
for block in form_blocks(func['instrs']):
# Mark the block.
leader = block[0]
if 'label' in leader:
print('block "{}":'.format(leader['label']))
block = block[1:] # Hide the label, for concision.
else:
print('anonymous block:')

# Print the instructions.
for instr in block:
print(' {}'.format(briltxt.instr_to_string(instr)))


if __name__ == '__main__':
print_blocks(json.load(sys.stdin))
104 changes: 104 additions & 0 deletions tasks/t7/tdce_hj424.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# input: .JSON stream
# output: .JSON stream - optimized with DCE, removes the unused duplicated assignments

import sys
import json
from form_blocks import form_blocks
from util import flatten

def opt_dce(func):

# global optimization
blocks = list(form_blocks(func['instrs']))

# collect useful variable list
used_dest = []
for block in blocks:
for element in block:
if "args" in element:
for op in element["args"]:
used_dest.append(op)
# remove duplicated element
used_dest = list(dict.fromkeys(used_dest))

# iterate all blocks
# delete unused assignment
for block in blocks:
opt_done = False
while(not opt_done):
num_instr = len(block)
# check all instructions and delete the unused ones
for element in block:
del_instr = True
if "dest" in element:
for op in used_dest:
if op == element["dest"]:
del_instr = False
else:
del_instr = False
# delete the useless instructions
if del_instr:
#print("+++"+str(element))
block.remove(element)
# if no changes, optimization done
if num_instr == len(block):
opt_done = True

# iterate all blocks
# delete duplicated assignment
# remove duplicated assignment for each block
for block in blocks:
opt_done = False
while(not opt_done):
num_instr = len(block)
# build the assigned but not used dictionary
assigned_not_used = {}
element_ptr = 0
for element in block:
# check the assigned list
if "dest" in element:
for key,value in assigned_not_used.items():
if element["dest"] == key:
# delete the old assignment
#print("==="+str(block[value]))
del block[value]
break
# add new assignment
if "dest" in element:
assigned_not_used[element["dest"]] = element_ptr
# update the assigned but not used dicionary
if "args" in element:
for op in element["args"]:
keys = []
for key,value in assigned_not_used.items():
if op == key:
keys.append(key)
break
for key in keys:
del assigned_not_used[key]
# update element pointer
element_ptr = element_ptr + 1

# if no changes, optimization done
if num_instr == len(block):
opt_done = True

# Reassemble the function.
func['instrs'] = flatten(blocks)

def opt_func(func):
opt_dce(func)

def local_opts():
# copied from example
# Apply the change to all the functions in the input program.
bril = json.load(sys.stdin)
# iterate all functions
for func in bril['functions']:
opt_func(func)
json.dump(bril, sys.stdout, indent=2, sort_keys=True)

# "real" main function
if __name__ == "__main__":
local_opts()

35 changes: 35 additions & 0 deletions tasks/t7/tests/branch.bril
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Branch example
# For evaluating a tracing JIT

# pseudo code
# main(x) {
# a = 1;
# if (x < 100) {
# y = y + 1;
# a = 100;
# }
# else {
# y = y - 1;
# }
# print y;
# }

@main (x: int) {
# const
vc0: int = const 1;
vc1: int = const 100;
# take two input ops, first iteration
v0: int = id x;
v1: int = id vc0;
v2: bool = lt v0 vc1;
br v2 .if.1 .else.1;
.if.1:
v3: int = add v0 vc0;
v1: int = id vc1;
jmp .program.end;
.else.1:
v3: int = sub v0 vc0;
# print out the results
.program.end:
print v3;
}
5 changes: 5 additions & 0 deletions tasks/t7/tests/branch_profile.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{ "profile" : [
{"args":["v2"],"labels":["if.1","else.1"],"op":"br"},
{"args":["v0","vc0"],"dest":"v3","op":"add","type":"int"},
{"args":["v0","vc0"],"dest":"v3","op":"sub","type":"int"}
] }
Loading

0 comments on commit ec0ebb4

Please sign in to comment.