:add lab7 spoc exercise related codes

This commit is contained in:
yuchen 2015-04-29 12:38:37 +08:00
parent 69afb3405b
commit f9b1bb3b66
14 changed files with 2643 additions and 0 deletions

View File

@ -0,0 +1,227 @@
# lab7 理解race condition
## x86模拟运行环境
x86.py是一个模拟执行基于汇编代码的多线程执行过程的模拟器。这里的汇编语法是基于很简单的x86汇编语法。
且没有包括OS的调度、context切换和中断处理过程。每条指令大小为一个byte。每个变量占4个byte
在硬件上模拟了4个通用寄存器
```
%ax, %bx, %cx, %dx
```
一个程序计数器`pc`,一个堆栈寄存器`sp`,和一小部分指令:
```
mov immediate, register # immediate value --> register
mov memory, register # memory --> register
mov register, register # register --> register
mov register, memory # register --> memory
mov immediate, memory # immediate value --> memory
add immediate, register # register = register + immediate
add register1, register2 # register2 = register2 + register1
sub immediate, register # register = register - immediate
sub register1, register2 # register2 = register2 - register1
test immediate, register # compare immediate and register (set condition codes)
test register, immediate # compare register and immediate (set condition codes)
test register, register # compare register and register (set condition codes)
jne # jump if test'd values are not equal
je # jump if test'd values are equal
jlt # jump if test'd second is less than first
jlte # jump if test'd second is less than or equal first
jgt # jump if test'd second is greater than first
jgte # jump if test'd second is greater than or equal first
xchg register, memory # atomic exchange:
# put value of register into memory
# return old contents of memory into reg
# do both things atomically
nop # no op
halt # stop
push memory or register # push value in memory or from reg onto stack
# stack is defined by sp register
pop [register] # pop value off stack (into optional register)
call label # call function at label
yield # switch to the next thread in the runqueue
```
注意:
- 'immediate' 格式是 $number
- 'memory' 格式是 'number' 或 '(reg)' 或 'number(reg)' 或 'number(reg,reg)'
- (%cx) -> 在括号中的register cx 的值 形成 address
- 2000 -> 2000 形成 address
- 1000(%dx) -> 1000 + dx的值 形成 address
- 10(%ax,%bx) -> 10 + ax的值 + bx的值 形成 address
- 'register' 格式是 %ax, %bx, %cx, %dx
下面是一个代码片段:
```
.main
mov 2000, %ax # 取地址2000处的内存单元的内容并赋值给ax
add $1, %ax # ax=ax+1
mov %ax, 2000 # 把ax的内容存储到地址2000处的内存单元中
halt
```
其含义如下
```
2000 -> 2000 形成地址 address
(%cx) -> cx的内容 形成地址 address
1000(%dx) -> 1000+dx 形成地址 address
10(%ax,%bx) -> (10+ax+bx) 形成地址 address
halt -> 执行结束
```
循环执行的小例子片段
```
.main
.top
sub $1,%dx
test $0,%dx
jgte .top
halt
```
x86.py模拟器运行参数
```
-h, --help show this help message and exit
-s SEED, --seed=SEED the random seed
-t NUMTHREADS, --threads=NUMTHREADS
number of threads
-p PROGFILE, --program=PROGFILE
source program (in .s)
-i INTFREQ, --interrupt=INTFREQ
interrupt frequency
-r, --randints if interrupts are random
-a ARGV, --argv=ARGV comma-separated per-thread args (e.g., ax=1,ax=2 sets
thread 0 ax reg to 1 and thread 1 ax reg to 2);
specify multiple regs per thread via colon-separated
list (e.g., ax=1:bx=2,cx=3 sets thread 0 ax and bx and
just cx for thread 1)
-L LOADADDR, --loadaddr=LOADADDR
address where to load code
-m MEMSIZE, --memsize=MEMSIZE
size of address space (KB)
-M MEMTRACE, --memtrace=MEMTRACE
comma-separated list of addrs to trace (e.g.,
20000,20001)
-R REGTRACE, --regtrace=REGTRACE
comma-separated list of regs to trace (e.g.,
ax,bx,cx,dx)
-C, --cctrace should we trace condition codes
-S, --printstats print some extra stats
-v, --verbose print some extra info
-c, --compute compute answers for me
```
执行举例
```
$ ./x86.py -p simple-race.s -t 1 -M 2000 -R ax,bx
2000 ax bx Thread 0
? ? ?
? ? ? 1000 mov 2000, %ax
? ? ? 1001 add $1, %ax
? ? ? 1002 mov %ax, 2000
? ? ? 1003 halt
```
如果加上参数 `-c`可得到具体执行结果
```
$ ./x86.py -p simple-race.s -t 1 -M 2000 -R ax,bx -c
2000 ax bx Thread 0
0 0 0
0 0 0 1000 mov 2000, %ax
0 1 0 1001 add $1, %ax
1 1 0 1002 mov %ax, 2000
1 1 0 1003 halt
```
另外一个执行的例子
```
$ ./x86.py -p loop.s -t 1 -a dx=3 -R dx -C -c
dx >= > <= < != == Thread 0
3 0 0 0 0 0 0
2 0 0 0 0 0 0 1000 sub $1,%dx
2 1 1 0 0 1 0 1001 test $0,%dx
2 1 1 0 0 1 0 1002 jgte .top
1 1 1 0 0 1 0 1000 sub $1,%dx
1 1 1 0 0 1 0 1001 test $0,%dx
1 1 1 0 0 1 0 1002 jgte .top
0 1 1 0 0 1 0 1000 sub $1,%dx
0 1 0 1 0 0 1 1001 test $0,%dx
0 1 0 1 0 0 1 1002 jgte .top
0 1 0 1 0 0 1 1003 halt
```
多线程存在race condition 的例子 looping-race-nolock.s
```
.main
.top
# critical section
mov 2000, %ax # get the value at the address
add $1, %ax # increment it
mov %ax, 2000 # store it back
# see if we're still looping
sub $1, %bx
test $0, %bx
jgt .top
halt
```
执行结果:
```
$ ./x86.py -p looping-race-nolock.s -t 2 -a bx=1 -M 2000 -c
2000 bx Thread 0 Thread 1
0 1
0 1 1000 mov 2000, %ax
0 1 1001 add $1, %ax
1 1 1002 mov %ax, 2000
1 0 1003 sub $1, %bx
1 0 1004 test $0, %bx
1 0 1005 jgt .top
1 0 1006 halt
1 1 ----- Halt;Switch ----- ----- Halt;Switch -----
1 1 1000 mov 2000, %ax
1 1 1001 add $1, %ax
2 1 1002 mov %ax, 2000
2 0 1003 sub $1, %bx
2 0 1004 test $0, %bx
2 0 1005 jgt .top
2 0 1006 halt
```
多线程存在 race condition 的例子 looping-race-nolock.s 在引入中断后会产生race condition.
```
$ ./x86.py -p looping-race-nolock.s -t 2 -a bx=1 -M 2000 -i 2
2000 Thread 0 Thread 1
?
? 1000 mov 2000, %ax
? 1001 add $1, %ax
? ------ Interrupt ------ ------ Interrupt ------
? 1000 mov 2000, %ax
? 1001 add $1, %ax
? ------ Interrupt ------ ------ Interrupt ------
? 1002 mov %ax, 2000
? 1003 sub $1, %bx
? ------ Interrupt ------ ------ Interrupt ------
? 1002 mov %ax, 2000
? 1003 sub $1, %bx
? ------ Interrupt ------ ------ Interrupt ------
? 1004 test $0, %bx
? 1005 jgt .top
? ------ Interrupt ------ ------ Interrupt ------
? 1004 test $0, %bx
? 1005 jgt .top
? ------ Interrupt ------ ------ Interrupt ------
? 1006 halt
? ----- Halt;Switch ----- ----- Halt;Switch -----
? 1006 halt
```

View File

@ -0,0 +1,6 @@
.main
.top
sub $1,%dx
test $0,%dx
jgte .top
halt

View File

@ -0,0 +1,15 @@
# assumes %bx has loop count in it
.main
.top
# critical section
mov 2000, %ax # get the value at the address
add $1, %ax # increment it
mov %ax, 2000 # store it back
# see if we're still looping
sub $1, %bx
test $0, %bx
jgt .top
halt

View File

@ -0,0 +1,6 @@
.main
# this is a critical section
mov 2000(%bx), %ax # get the value at the address
add $1, %ax # increment it
mov %ax, 2000(%bx) # store it back
halt

View File

@ -0,0 +1,7 @@
.main
mov $9,%dx
.top
sub $1,%dx
test $0,%dx
jgte .top
halt

View File

@ -0,0 +1,13 @@
.main
test $1, %ax # ax should be 1 (signaller) or 0 (waiter)
je .signaller
.waiter
mov 2000, %cx
test $1, %cx
jne .waiter
halt
.signaller
mov $1, 2000
halt

View File

@ -0,0 +1,989 @@
#! /usr/bin/env python
import sys
import time
import random
from optparse import OptionParser
#
# HELPER
#
def dospace(howmuch):
for i in range(howmuch):
print '%24s' % ' ',
# useful instead of assert
def zassert(cond, str):
if cond == False:
print 'ABORT::', str
exit(1)
return
class cpu:
#
# INIT: how much memory?
#
def __init__(self, memory, memtrace, regtrace, cctrace, compute, verbose):
#
# CONSTANTS
#
# conditions
self.COND_GT = 0
self.COND_GTE = 1
self.COND_LT = 2
self.COND_LTE = 3
self.COND_EQ = 4
self.COND_NEQ = 5
# registers in system
self.REG_ZERO = 0
self.REG_AX = 1
self.REG_BX = 2
self.REG_CX = 3
self.REG_DX = 4
self.REG_SP = 5
self.REG_BP = 6
# system memory: in KB
self.max_memory = memory * 1024
# which memory addrs and registers to trace?
self.memtrace = memtrace
self.regtrace = regtrace
self.cctrace = cctrace
self.compute = compute
self.verbose = verbose
self.PC = 0
self.registers = {}
self.conditions = {}
self.labels = {}
self.vars = {}
self.memory = {}
self.pmemory = {} # for printable version of what's in memory (instructions)
self.condlist = [self.COND_GTE, self.COND_GT, self.COND_LTE, self.COND_LT, self.COND_NEQ, self.COND_EQ]
self.regnums = [self.REG_ZERO, self.REG_AX, self.REG_BX, self.REG_CX, self.REG_DX, self.REG_SP, self.REG_BP]
self.regnames = {}
self.regnames['zero'] = self.REG_ZERO # hidden zero-valued register
self.regnames['ax'] = self.REG_AX
self.regnames['bx'] = self.REG_BX
self.regnames['cx'] = self.REG_CX
self.regnames['dx'] = self.REG_DX
self.regnames['sp'] = self.REG_SP
self.regnames['bp'] = self.REG_BP
tmplist = []
for r in self.regtrace:
zassert(r in self.regnames, 'Register %s cannot be traced because it does not exist' % r)
tmplist.append(self.regnames[r])
self.regtrace = tmplist
self.init_memory()
self.init_registers()
self.init_condition_codes()
#
# BEFORE MACHINE RUNS
#
def init_condition_codes(self):
for c in self.condlist:
self.conditions[c] = False
def init_memory(self):
for i in range(self.max_memory):
self.memory[i] = 0
def init_registers(self):
for i in self.regnums:
self.registers[i] = 0
def dump_memory(self):
print 'MEMORY DUMP'
for i in range(self.max_memory):
if i not in self.pmemory and i in self.memory and self.memory[i] != 0:
print ' m[%d]' % i, self.memory[i]
#
# INFORMING ABOUT THE HARDWARE
#
def get_regnum(self, name):
assert(name in self.regnames)
return self.regnames[name]
def get_regname(self, num):
assert(num in self.regnums)
for rname in self.regnames:
if self.regnames[rname] == num:
return rname
return ''
def get_regnums(self):
return self.regnums
def get_condlist(self):
return self.condlist
def get_reg(self, reg):
assert(reg in self.regnums)
return self.registers[reg]
def get_cond(self, cond):
assert(cond in self.condlist)
return self.conditions[cond]
def get_pc(self):
return self.PC
def set_reg(self, reg, value):
assert(reg in self.regnums)
self.registers[reg] = value
def set_cond(self, cond, value):
assert(cond in self.condlist)
self.conditions[cond] = value
def set_pc(self, pc):
self.PC = pc
#
# INSTRUCTIONS
#
def halt(self):
return -1
def iyield(self):
return -2
def nop(self):
return 0
def rdump(self):
print 'REGISTERS::',
print 'ax:', self.registers[self.REG_AX],
print 'bx:', self.registers[self.REG_BX],
print 'cx:', self.registers[self.REG_CX],
print 'dx:', self.registers[self.REG_DX],
def mdump(self, index):
print 'm[%d] ' % index, self.memory[index]
def move_i_to_r(self, src, dst):
self.registers[dst] = src
return 0
# memory: value, register, register
def move_i_to_m(self, src, value, reg1, reg2):
tmp = value + self.registers[reg1] + self.registers[reg2]
self.memory[tmp] = src
return 0
def move_m_to_r(self, value, reg1, reg2, dst):
tmp = value + self.registers[reg1] + self.registers[reg2]
# print 'doing mov', 'val:', value, 'r1:', self.get_regname(reg1), self.registers[reg1], 'r2:', self.get_regname(reg2), self.registers[reg2], 'dst', self.get_regname(dst), 'tmp', tmp, 'reg[dst]', self.registers[dst], 'mem', self.memory[tmp]
self.registers[dst] = self.memory[tmp]
def move_r_to_m(self, src, value, reg1, reg2):
tmp = value + self.registers[reg1] + self.registers[reg2]
self.memory[tmp] = self.registers[src]
return 0
def move_r_to_r(self, src, dst):
self.registers[dst] = self.registers[src]
return 0
def add_i_to_r(self, src, dst):
self.registers[dst] += src
return 0
def add_r_to_r(self, src, dst):
self.registers[dst] += self.registers[src]
return 0
def sub_i_to_r(self, src, dst):
self.registers[dst] -= src
return 0
def sub_r_to_r(self, src, dst):
self.registers[dst] -= self.registers[src]
return 0
#
# SUPPORT FOR LOCKS
#
def atomic_exchange(self, src, value, reg1, reg2):
tmp = value + self.registers[reg1] + self.registers[reg2]
old = self.memory[tmp]
self.memory[tmp] = self.registers[src]
self.registers[src] = old
return 0
def fetchadd(self, src, value, reg1, reg2):
tmp = value + self.registers[reg1] + self.registers[reg2]
old = self.memory[tmp]
self.memory[tmp] = self.memory[tmp] + self.registers[src]
self.registers[src] = old
#
# TEST for conditions
#
def test_all(self, src, dst):
self.init_condition_codes()
if dst > src:
self.conditions[self.COND_GT] = True
if dst >= src:
self.conditions[self.COND_GTE] = True
if dst < src:
self.conditions[self.COND_LT] = True
if dst <= src:
self.conditions[self.COND_LTE] = True
if dst == src:
self.conditions[self.COND_EQ] = True
if dst != src:
self.conditions[self.COND_NEQ] = True
return 0
def test_i_r(self, src, dst):
self.init_condition_codes()
return self.test_all(src, self.registers[dst])
def test_r_i(self, src, dst):
self.init_condition_codes()
return self.test_all(self.registers[src], dst)
def test_r_r(self, src, dst):
self.init_condition_codes()
return self.test_all(self.registers[src], self.registers[dst])
#
# JUMPS
#
def jump(self, targ):
self.PC = targ
return 0
def jump_notequal(self, targ):
if self.conditions[self.COND_NEQ] == True:
self.PC = targ
return 0
def jump_equal(self, targ):
if self.conditions[self.COND_EQ] == True:
self.PC = targ
return 0
def jump_lessthan(self, targ):
if self.conditions[self.COND_LT] == True:
self.PC = targ
return 0
def jump_lessthanorequal(self, targ):
if self.conditions[self.COND_LTE] == True:
self.PC = targ
return 0
def jump_greaterthan(self, targ):
if self.conditions[self.COND_GT] == True:
self.PC = targ
return 0
def jump_greaterthanorequal(self, targ):
if self.conditions[self.COND_GTE] == True:
self.PC = targ
return 0
#
# CALL and RETURN
#
def call(self, targ):
self.registers[self.REG_SP] -= 4
self.memory[self.registers[self.REG_SP]] = self.PC
self.PC = targ
def ret(self):
self.PC = self.memory[self.registers[self.REG_SP]]
self.registers[self.REG_SP] += 4
#
# STACK and related
#
def push_r(self, reg):
self.registers[self.REG_SP] -= 4
self.memory[self.registers[self.REG_SP]] = self.registers[reg]
return 0
def push_m(self, value, reg1, reg2):
# print 'push_m', value, reg1, reg2
self.registers[self.REG_SP] -= 4
tmp = value + self.registers[reg1] + self.registers[reg2]
# push address onto stack, not memory value itself
self.memory[self.registers[self.REG_SP]] = tmp
return 0
def pop(self):
self.registers[self.REG_SP] += 4
def pop_r(self, dst):
self.registers[dst] = self.registers[self.REG_SP]
self.registers[self.REG_SP] += 4
#
# HELPER func for getarg
#
def register_translate(self, r):
if r in self.regnames:
return self.regnames[r]
zassert(False, 'Register %s is not a valid register' % r)
return
#
# HELPER in parsing mov (quite primitive) and other ops
# returns: (value, type)
# where type is (TYPE_REGISTER, TYPE_IMMEDIATE, TYPE_MEMORY)
#
# FORMATS
# %ax - register
# $10 - immediate
# 10 - direct memory
# 10(%ax) - memory + reg indirect
# 10(%ax,%bx) - memory + 2 reg indirect
# 10(%ax,%bx,4) - XXX (not handled)
#
def getarg(self, arg):
tmp1 = arg.replace(',', '')
tmp = tmp1.replace(' \t', '')
if tmp[0] == '$':
zassert(len(tmp) == 2, 'correct form is $number (not %s)' % tmp)
value = tmp.split('$')[1]
zassert(value.isdigit(), 'value [%s] must be a digit' % value)
return int(value), 'TYPE_IMMEDIATE'
elif tmp[0] == '%':
register = tmp.split('%')[1]
return self.register_translate(register), 'TYPE_REGISTER'
elif tmp[0] == '(':
register = tmp.split('(')[1].split(')')[0].split('%')[1]
return '%d,%d,%d' % (0, self.register_translate(register), self.register_translate('zero')), 'TYPE_MEMORY'
elif tmp[0] == '.':
targ = tmp
return targ, 'TYPE_LABEL'
elif tmp[0].isalpha() and not tmp[0].isdigit():
zassert(tmp in self.vars, 'Variable %s is not declared' % tmp)
# print '%d,%d,%d' % (self.vars[tmp], self.register_translate('zero'), self.register_translate('zero')), 'TYPE_MEMORY'
return '%d,%d,%d' % (self.vars[tmp], self.register_translate('zero'), self.register_translate('zero')), 'TYPE_MEMORY'
elif tmp[0].isdigit() or tmp[0] == '-':
# MOST GENERAL CASE: number(reg,reg) or number(reg)
# we ignore the common x86 number(reg,reg,constant) for now
neg = 1
if tmp[0] == '-':
tmp = tmp[1:]
neg = -1
s = tmp.split('(')
if len(s) == 1:
value = neg * int(tmp)
# print '%d,%d,%d' % (int(value), self.register_translate('zero'), self.register_translate('zero')), 'TYPE_MEMORY'
return '%d,%d,%d' % (int(value), self.register_translate('zero'), self.register_translate('zero')), 'TYPE_MEMORY'
elif len(s) == 2:
value = neg * int(s[0])
t = s[1].split(')')[0].split(',')
if len(t) == 1:
register = t[0].split('%')[1]
# print '%d,%d,%d' % (int(value), self.register_translate(register), self.register_translate('zero')), 'TYPE_MEMORY'
return '%d,%d,%d' % (int(value), self.register_translate(register), self.register_translate('zero')), 'TYPE_MEMORY'
elif len(t) == 2:
register1 = t[0].split('%')[1]
register2 = t[1].split('%')[1]
# print '%d,%d,%d' % (int(value), self.register_translate(register1), self.register_translate(register2)), 'TYPE_MEMORY'
return '%d,%d,%d' % (int(value), self.register_translate(register1), self.register_translate(register2)), 'TYPE_MEMORY'
else:
print 'mov: bad argument [%s]' % tmp
exit(1)
return
zassert(True, 'mov: bad argument [%s]' % arg)
return
#
# LOAD a program into memory
# make it ready to execute
#
def load(self, infile, loadaddr):
pc = int(loadaddr)
fd = open(infile)
bpc = loadaddr
data = 100
for line in fd:
cline = line.rstrip()
# print 'PASS 1', cline
# remove everything after the comment marker
ctmp = cline.split('#')
assert(len(ctmp) == 1 or len(ctmp) == 2)
if len(ctmp) == 2:
cline = ctmp[0]
# remove empty lines, and split line by spaces
tmp = cline.split()
if len(tmp) == 0:
continue
# only pay attention to labels and variables
if tmp[0] == '.var':
assert(len(tmp) == 2)
assert(tmp[0] not in self.vars)
self.vars[tmp[1]] = data
data += 4
zassert(data < bpc, 'Load address overrun by static data')
if self.verbose: print 'ASSIGN VAR', tmp[0], "-->", tmp[1], self.vars[tmp[1]]
elif tmp[0][0] == '.':
assert(len(tmp) == 1)
self.labels[tmp[0]] = int(pc)
if self.verbose: print 'ASSIGN LABEL', tmp[0], "-->", pc
else:
pc += 1
fd.close()
if self.verbose: print ''
# second pass: do everything else
pc = int(loadaddr)
fd = open(infile)
for line in fd:
cline = line.rstrip()
# print 'PASS 2', cline
# remove everything after the comment marker
ctmp = cline.split('#')
assert(len(ctmp) == 1 or len(ctmp) == 2)
if len(ctmp) == 2:
cline = ctmp[0]
# remove empty lines, and split line by spaces
tmp = cline.split()
if len(tmp) == 0:
continue
# skip labels: all else must be instructions
if cline[0] != '.':
tmp = cline.split(None, 1)
opcode = tmp[0]
self.pmemory[pc] = cline.strip()
# MAIN OPCODE LOOP
if opcode == 'mov':
rtmp = tmp[1].split(',', 1)
zassert(len(tmp) == 2 and len(rtmp) == 2, 'mov: needs two args, separated by commas [%s]' % cline)
arg1 = rtmp[0].strip()
arg2 = rtmp[1].strip()
(src, stype) = self.getarg(arg1)
(dst, dtype) = self.getarg(arg2)
# print 'MOV', src, stype, dst, dtype
if stype == 'TYPE_MEMORY' and dtype == 'TYPE_MEMORY':
print 'bad mov: two memory arguments'
exit(1)
elif stype == 'TYPE_IMMEDIATE' and dtype == 'TYPE_IMMEDIATE':
print 'bad mov: two immediate arguments'
exit(1)
elif stype == 'TYPE_IMMEDIATE' and dtype == 'TYPE_REGISTER':
self.memory[pc] = 'self.move_i_to_r(%d, %d)' % (int(src), dst)
elif stype == 'TYPE_IMMEDIATE' and dtype == 'TYPE_REGISTER':
self.memory[pc] = 'self.move_i_to_r(%d, %d)' % (int(src), dst)
elif stype == 'TYPE_MEMORY' and dtype == 'TYPE_REGISTER':
tmp = src.split(',')
assert(len(tmp) == 3)
self.memory[pc] = 'self.move_m_to_r(%d, %d, %d, %d)' % (int(tmp[0]), int(tmp[1]), int(tmp[2]), dst)
elif stype == 'TYPE_REGISTER' and dtype == 'TYPE_MEMORY':
tmp = dst.split(',')
assert(len(tmp) == 3)
self.memory[pc] = 'self.move_r_to_m(%d, %d, %d, %d)' % (src, int(tmp[0]), int(tmp[1]), int(tmp[2]))
elif stype == 'TYPE_REGISTER' and dtype == 'TYPE_REGISTER':
self.memory[pc] = 'self.move_r_to_r(%d, %d)' % (src, dst)
elif stype == 'TYPE_IMMEDIATE' and dtype == 'TYPE_MEMORY':
tmp = dst.split(',')
assert(len(tmp) == 3)
self.memory[pc] = 'self.move_i_to_m(%d, %d, %d, %d)' % (src, int(tmp[0]), int(tmp[1]), int(tmp[2]))
else:
zassert(False, 'malformed mov instruction')
elif opcode == 'pop':
if len(tmp) == 1:
self.memory[pc] = 'self.pop()'
elif len(tmp) == 2:
arg = tmp[1].strip()
(dst, dtype) = self.getarg(arg)
zassert(dtype == 'TYPE_REGISTER', 'Can only pop into a register')
self.memory[pc] = 'self.pop_r(%d)' % dst
else:
zassert(False, 'pop instruction must take zero/one args')
elif opcode == 'push':
(src, stype) = self.getarg(tmp[1].strip())
if stype == 'TYPE_REGISTER':
self.memory[pc] = 'self.push_r(%d)' % (int(src))
elif stype == 'TYPE_MEMORY':
tmp = src.split(',')
assert(len(tmp) == 3)
self.memory[pc] = 'self.push_m(%d,%d,%d)' % (int(tmp[0]), int(tmp[1]), int(tmp[2]))
else:
zassert(False, 'Cannot push anything but registers')
elif opcode == 'call':
(targ, ttype) = self.getarg(tmp[1].strip())
if ttype == 'TYPE_LABEL':
self.memory[pc] = 'self.call(%d)' % (int(self.labels[targ]))
else:
zassert(False, 'Cannot call anything but a label')
elif opcode == 'ret':
assert(len(tmp) == 1)
self.memory[pc] = 'self.ret()'
elif opcode == 'add':
rtmp = tmp[1].split(',', 1)
zassert(len(tmp) == 2 and len(rtmp) == 2, 'add: needs two args, separated by commas [%s]' % cline)
arg1 = rtmp[0].strip()
arg2 = rtmp[1].strip()
(src, stype) = self.getarg(arg1)
(dst, dtype) = self.getarg(arg2)
if stype == 'TYPE_IMMEDIATE' and dtype == 'TYPE_REGISTER':
self.memory[pc] = 'self.add_i_to_r(%d, %d)' % (int(src), dst)
elif stype == 'TYPE_REGISTER' and dtype == 'TYPE_REGISTER':
self.memory[pc] = 'self.add_r_to_r(%d, %d)' % (int(src), dst)
else:
zassert(False, 'malformed usage of add instruction')
elif opcode == 'sub':
rtmp = tmp[1].split(',', 1)
zassert(len(tmp) == 2 and len(rtmp) == 2, 'sub: needs two args, separated by commas [%s]' % cline)
arg1 = rtmp[0].strip()
arg2 = rtmp[1].strip()
(src, stype) = self.getarg(arg1)
(dst, dtype) = self.getarg(arg2)
if stype == 'TYPE_IMMEDIATE' and dtype == 'TYPE_REGISTER':
self.memory[pc] = 'self.sub_i_to_r(%d, %d)' % (int(src), dst)
elif stype == 'TYPE_REGISTER' and dtype == 'TYPE_REGISTER':
self.memory[pc] = 'self.sub_r_to_r(%d, %d)' % (int(src), dst)
else:
zassert(False, 'malformed usage of sub instruction')
elif opcode == 'fetchadd':
rtmp = tmp[1].split(',', 1)
zassert(len(tmp) == 2 and len(rtmp) == 2, 'fetchadd: needs two args, separated by commas [%s]' % cline)
arg1 = rtmp[0].strip()
arg2 = rtmp[1].strip()
(src, stype) = self.getarg(arg1)
(dst, dtype) = self.getarg(arg2)
tmp = dst.split(',')
assert(len(tmp) == 3)
if stype == 'TYPE_REGISTER' and dtype == 'TYPE_MEMORY':
self.memory[pc] = 'self.fetchadd(%d, %d, %d, %d)' % (src, int(tmp[0]), int(tmp[1]), int(tmp[2]))
else:
zassert(False, 'poorly specified fetch and add')
elif opcode == 'xchg':
rtmp = tmp[1].split(',', 1)
zassert(len(tmp) == 2 and len(rtmp) == 2, 'xchg: needs two args, separated by commas [%s]' % cline)
arg1 = rtmp[0].strip()
arg2 = rtmp[1].strip()
(src, stype) = self.getarg(arg1)
(dst, dtype) = self.getarg(arg2)
tmp = dst.split(',')
assert(len(tmp) == 3)
if stype == 'TYPE_REGISTER' and dtype == 'TYPE_MEMORY':
self.memory[pc] = 'self.atomic_exchange(%d, %d, %d, %d)' % (src, int(tmp[0]), int(tmp[1]), int(tmp[2]))
else:
zassert(False, 'poorly specified atomic exchange')
elif opcode == 'test':
rtmp = tmp[1].split(',', 1)
zassert(len(tmp) == 2 and len(rtmp) == 2, 'test: needs two args, separated by commas [%s]' % cline)
arg1 = rtmp[0].strip()
arg2 = rtmp[1].strip()
(src, stype) = self.getarg(arg1)
(dst, dtype) = self.getarg(arg2)
if stype == 'TYPE_IMMEDIATE' and dtype == 'TYPE_REGISTER':
self.memory[pc] = 'self.test_i_r(%d, %d)' % (int(src), dst)
elif stype == 'TYPE_REGISTER' and dtype == 'TYPE_REGISTER':
self.memory[pc] = 'self.test_r_r(%d, %d)' % (int(src), dst)
elif stype == 'TYPE_REGISTER' and dtype == 'TYPE_IMMEDIATE':
self.memory[pc] = 'self.test_r_i(%d, %d)' % (int(src), dst)
else:
zassert(False, 'malformed usage of test instruction')
elif opcode == 'j':
(targ, ttype) = self.getarg(tmp[1].strip())
zassert(ttype == 'TYPE_LABEL', 'bad jump target [%s]' % tmp[1].strip())
self.memory[pc] = 'self.jump(%d)' % int(self.labels[targ])
elif opcode == 'jne':
(targ, ttype) = self.getarg(tmp[1].strip())
zassert(ttype == 'TYPE_LABEL', 'bad jump target [%s]' % tmp[1].strip())
self.memory[pc] = 'self.jump_notequal(%d)' % int(self.labels[targ])
elif opcode == 'je':
(targ, ttype) = self.getarg(tmp[1].strip())
zassert(ttype == 'TYPE_LABEL', 'bad jump target [%s]' % tmp[1].strip())
self.memory[pc] = 'self.jump_equal(%d)' % self.labels[targ]
elif opcode == 'jlt':
(targ, ttype) = self.getarg(tmp[1].strip())
zassert(ttype == 'TYPE_LABEL', 'bad jump target [%s]' % tmp[1].strip())
self.memory[pc] = 'self.jump_lessthan(%d)' % int(self.labels[targ])
elif opcode == 'jlte':
(targ, ttype) = self.getarg(tmp[1].strip())
zassert(ttype == 'TYPE_LABEL', 'bad jump target [%s]' % tmp[1].strip())
self.memory[pc] = 'self.jump_lessthanorequal(%s)' % self.labels[targ]
elif opcode == 'jgt':
(targ, ttype) = self.getarg(tmp[1].strip())
zassert(ttype == 'TYPE_LABEL', 'bad jump target [%s]' % tmp[1].strip())
self.memory[pc] = 'self.jump_greaterthan(%d)' % int(self.labels[targ])
elif opcode == 'jgte':
(targ, ttype) = self.getarg(tmp[1].strip())
zassert(ttype == 'TYPE_LABEL', 'bad jump target [%s]' % tmp[1].strip())
self.memory[pc] = 'self.jump_greaterthanorequal(%s)' % self.labels[targ]
elif opcode == 'nop':
self.memory[pc] = 'self.nop()'
elif opcode == 'halt':
self.memory[pc] = 'self.halt()'
elif opcode == 'yield':
self.memory[pc] = 'self.iyield()'
elif opcode == 'rdump':
self.memory[pc] = 'self.rdump()'
elif opcode == 'mdump':
self.memory[pc] = 'self.mdump(%s)' % tmp[1]
else:
print 'illegal opcode: ', opcode
exit(1)
if self.verbose: print 'pc:%d LOADING %20s --> %s' % (pc, self.pmemory[pc], self.memory[pc])
# INCREMENT PC for loader
pc += 1
# END: loop over file
fd.close()
if self.verbose: print ''
return
# END: load
def print_headers(self, procs):
# print some headers
if len(self.memtrace) > 0:
for m in self.memtrace:
if m[0].isdigit():
print '%5d' % int(m),
else:
zassert(m in self.vars, 'Traced variable %s not declared' % m)
print '%5s' % m,
print ' ',
if len(self.regtrace) > 0:
for r in self.regtrace:
print '%5s' % self.get_regname(r),
print ' ',
if cctrace == True:
print '>= > <= < != ==',
# and per thread
for i in range(procs.getnum()):
print ' Thread %d ' % i,
print ''
return
def print_trace(self, newline):
if len(self.memtrace) > 0:
for m in self.memtrace:
if self.compute:
if m[0].isdigit():
print '%5d' % self.memory[int(m)],
else:
zassert(m in self.vars, 'Traced variable %s not declared' % m)
print '%5d' % self.memory[self.vars[m]],
else:
print '%5s' % '?',
print ' ',
if len(self.regtrace) > 0:
for r in self.regtrace:
if self.compute:
print '%5d' % self.registers[r],
else:
print '%5s' % '?',
print ' ',
if cctrace == True:
for c in self.condlist:
if self.compute:
if self.conditions[c]:
print '1 ',
else:
print '0 ',
else:
print '? ',
if (len(self.memtrace) > 0 or len(self.regtrace) > 0 or cctrace == True) and newline == True:
print ''
return
def setint(self, intfreq, intrand):
if intrand == False:
return intfreq
return int(random.random() * intfreq) + 1
def run(self, procs, intfreq, intrand):
# hw init: cc's, interrupt frequency, etc.
interrupt = self.setint(intfreq, intrand)
icount = 0
self.print_headers(procs)
self.print_trace(True)
while True:
# need thread ID of current process
tid = procs.getcurr().gettid()
# FETCH
prevPC = self.PC
instruction = self.memory[self.PC]
self.PC += 1
# DECODE and EXECUTE
# key: self.PC may be changed during eval; thus MUST be incremented BEFORE eval
rc = eval(instruction)
# tracing details: ALWAYS AFTER EXECUTION OF INSTRUCTION
self.print_trace(False)
# output: thread-proportional spacing followed by PC and instruction
dospace(tid)
print prevPC, self.pmemory[prevPC]
icount += 1
# halt instruction issued
if rc == -1:
procs.done()
if procs.numdone() == procs.getnum():
return icount
procs.next()
procs.restore()
self.print_trace(False)
for i in range(procs.getnum()):
print '----- Halt;Switch ----- ',
print ''
# do interrupt processing
interrupt -= 1
if interrupt == 0 or rc == -2:
interrupt = self.setint(intfreq, intrand)
procs.save()
procs.next()
procs.restore()
self.print_trace(False)
for i in range(procs.getnum()):
print '------ Interrupt ------ ',
print ''
# END: while
return
#
# END: class cpu
#
#
# PROCESS LIST class
#
class proclist:
def __init__(self):
self.plist = []
self.curr = 0
self.active = 0
def done(self):
self.plist[self.curr].setdone()
self.active -= 1
def numdone(self):
return len(self.plist) - self.active
def getnum(self):
return len(self.plist)
def add(self, p):
self.active += 1
self.plist.append(p)
def getcurr(self):
return self.plist[self.curr]
def save(self):
self.plist[self.curr].save()
def restore(self):
self.plist[self.curr].restore()
def next(self):
for i in range(self.curr+1, len(self.plist)):
if self.plist[i].isdone() == False:
self.curr = i
return
for i in range(0, self.curr+1):
if self.plist[i].isdone() == False:
self.curr = i
return
#
# PROCESS class
#
class process:
def __init__(self, cpu, tid, pc, stackbottom, reginit):
self.cpu = cpu # object reference
self.tid = tid
self.pc = pc
self.regs = {}
self.cc = {}
self.done = False
self.stack = stackbottom
# init regs: all 0 or specially set to something
for r in self.cpu.get_regnums():
self.regs[r] = 0
if reginit != '':
# form: ax=1,bx=2 (for some subset of registers)
for r in reginit.split(':'):
tmp = r.split('=')
assert(len(tmp) == 2)
self.regs[self.cpu.get_regnum(tmp[0])] = int(tmp[1])
# init CCs
for c in self.cpu.get_condlist():
self.cc[c] = False
# stack
self.regs[self.cpu.get_regnum('sp')] = stackbottom
# print 'REG', self.cpu.get_regnum('sp'), self.regs[self.cpu.get_regnum('sp')]
return
def gettid(self):
return self.tid
def save(self):
self.pc = self.cpu.get_pc()
for c in self.cpu.get_condlist():
self.cc[c] = self.cpu.get_cond(c)
for r in self.cpu.get_regnums():
self.regs[r] = self.cpu.get_reg(r)
def restore(self):
self.cpu.set_pc(self.pc)
for c in self.cpu.get_condlist():
self.cpu.set_cond(c, self.cc[c])
for r in self.cpu.get_regnums():
self.cpu.set_reg(r, self.regs[r])
def setdone(self):
self.done = True
def isdone(self):
return self.done == True
#
# main program
#
parser = OptionParser()
parser.add_option('-s', '--seed', default=0, help='the random seed', action='store', type='int', dest='seed')
parser.add_option('-t', '--threads', default=2, help='number of threads', action='store', type='int', dest='numthreads')
parser.add_option('-p', '--program', default='', help='source program (in .s)', action='store', type='string', dest='progfile')
parser.add_option('-i', '--interrupt', default=50, help='interrupt frequency', action='store', type='int', dest='intfreq')
parser.add_option('-r', '--randints', default=False, help='if interrupts are random', action='store_true', dest='intrand')
parser.add_option('-a', '--argv', default='',
help='comma-separated per-thread args (e.g., ax=1,ax=2 sets thread 0 ax reg to 1 and thread 1 ax reg to 2); specify multiple regs per thread via colon-separated list (e.g., ax=1:bx=2,cx=3 sets thread 0 ax and bx and just cx for thread 1)',
action='store', type='string', dest='argv')
parser.add_option('-L', '--loadaddr', default=1000, help='address where to load code', action='store', type='int', dest='loadaddr')
parser.add_option('-m', '--memsize', default=128, help='size of address space (KB)', action='store', type='int', dest='memsize')
parser.add_option('-M', '--memtrace', default='', help='comma-separated list of addrs to trace (e.g., 20000,20001)', action='store',
type='string', dest='memtrace')
parser.add_option('-R', '--regtrace', default='', help='comma-separated list of regs to trace (e.g., ax,bx,cx,dx)', action='store',
type='string', dest='regtrace')
parser.add_option('-C', '--cctrace', default=False, help='should we trace condition codes', action='store_true', dest='cctrace')
parser.add_option('-S', '--printstats',default=False, help='print some extra stats', action='store_true', dest='printstats')
parser.add_option('-v', '--verbose', default=False, help='print some extra info', action='store_true', dest='verbose')
parser.add_option('-c', '--compute', default=False, help='compute answers for me', action='store_true', dest='solve')
(options, args) = parser.parse_args()
print 'ARG seed', options.seed
print 'ARG numthreads', options.numthreads
print 'ARG program', options.progfile
print 'ARG interrupt frequency', options.intfreq
print 'ARG interrupt randomness',options.intrand
print 'ARG argv', options.argv
print 'ARG load address', options.loadaddr
print 'ARG memsize', options.memsize
print 'ARG memtrace', options.memtrace
print 'ARG regtrace', options.regtrace
print 'ARG cctrace', options.cctrace
print 'ARG printstats', options.printstats
print 'ARG verbose', options.verbose
print ''
seed = int(options.seed)
numthreads = int(options.numthreads)
intfreq = int(options.intfreq)
zassert(intfreq > 0, 'Interrupt frequency must be greater than 0')
intrand = int(options.intrand)
progfile = options.progfile
zassert(progfile != '', 'Program file must be specified')
argv = options.argv.split(',')
zassert(len(argv) == numthreads or len(argv) == 1, 'argv: must be one per-thread or just one set of values for all threads')
loadaddr = options.loadaddr
memsize = options.memsize
memtrace = []
if options.memtrace != '':
for m in options.memtrace.split(','):
memtrace.append(m)
regtrace = []
if options.regtrace != '':
for r in options.regtrace.split(','):
regtrace.append(r)
cctrace = options.cctrace
printstats = options.printstats
verbose = options.verbose
#
# MAIN program
#
debug = False
debug = False
cpu = cpu(memsize, memtrace, regtrace, cctrace, options.solve, verbose)
# load a program
cpu.load(progfile, loadaddr)
# process list
procs = proclist()
pid = 0
stack = memsize * 1000
for t in range(numthreads):
if len(argv) > 1:
arg = argv[pid]
else:
arg = argv[0]
procs.add(process(cpu, pid, loadaddr, stack, arg))
stack -= 1000
pid += 1
# get first one ready!
procs.restore()
# run it
t1 = time.clock()
ic = cpu.run(procs, intfreq, intrand)
t2 = time.clock()
if printstats:
print ''
print 'STATS:: Instructions %d' % ic
print 'STATS:: Emulation Rate %.2f kinst/sec' % (float(ic) / float(t2 - t1) / 1000.0)
# use this for profiling
# import cProfile
# cProfile.run('run()')

View File

@ -0,0 +1,27 @@
.var flag
.var count
.main
.top
.acquire
mov flag, %ax # get flag
test $0, %ax # if we get 0 back: lock is free!
jne .acquire # if not, try again
mov $1, flag # store 1 into flag
# critical section
mov count, %ax # get the value at the address
add $1, %ax # increment it
mov %ax, count # store it back
# release lock
mov $0, flag # clear the flag now
# see if we're still looping
sub $1, %bx
test $0, %bx
jgt .top
halt

View File

@ -0,0 +1,53 @@
# array of 2 integers (each size 4 bytes)
# load address of flag into fx register
# access flag[] with 0(%fx,%index,4)
# where %index is a register holding 0 or 1
# index reg contains 0 -> flag[0], if 1->flag[1]
.var flag 2
# global turn variable
.var turn
# global count
.var count
.main
# put address of flag into fx
lea flag, %fx
# assume thread ID is in bx (0 or 1, scale by 4 to get proper flag address)
mov %bx, %cx # bx: self, now copies to cx
neg %cx # cx: - self
add $1, %cx # cx: 1 - self
.acquire
mov $1, 0(%fx,%bx,4) # flag[self] = 1
mov %cx, turn # turn = 1 - self
.spin1
mov 0(%fx,%cx,4), %ax # flag[1-self]
test $1, %ax
jne .fini # if flag[1-self] != 1, skip past loop to .fini
.spin2 # just labeled for fun, not needed
mov turn, %ax
test %cx, %ax # compare 'turn' and '1 - self'
je .spin1 # if turn==1-self, go back and start spin again
# fall out of spin
.fini
# do critical section now
mov count, %ax
add $1, %ax
mov %ax, count
.release
mov $0, 0(%fx,%bx,4) # flag[self] = 0
# end case: make sure it's other's turn
mov %cx, turn # turn = 1 - self
halt

View File

@ -0,0 +1,26 @@
.var mutex
.var count
.main
.top
.acquire
mov $1, %ax
xchg %ax, mutex # atomic swap of 1 and mutex
test $0, %ax # if we get 0 back: lock is free!
jne .acquire # if not, try again
# critical section
mov count, %ax # get the value at the address
add $1, %ax # increment it
mov %ax, count # store it back
# release lock
mov $0, mutex
# see if we're still looping
sub $1, %bx
test $0, %bx
jgt .top
halt

View File

@ -0,0 +1,29 @@
.var mutex
.var count
.main
.top
.acquire
mov mutex, %ax
test $0, %ax
jne .acquire
mov $1, %ax
xchg %ax, mutex # atomic swap of 1 and mutex
test $0, %ax # if we get 0 back: lock is free!
jne .acquire # if not, try again
# critical section
mov count, %ax # get the value at the address
add $1, %ax # increment it
mov %ax, count # store it back
# release lock
mov $0, mutex
# see if we're still looping
sub $1, %bx
test $0, %bx
jgt .top
halt

View File

@ -0,0 +1,30 @@
.var ticket
.var turn
.var count
.main
.top
.acquire
mov $1, %ax
fetchadd %ax, ticket # grab a ticket (keep it in dx)
.tryagain
mov turn, %cx # check if it's your turn
test %cx, %ax
jne .tryagain
# critical section
mov count, %ax # get the value at the address
add $1, %ax # increment it
mov %ax, count # store it back
# release lock
mov $1, %ax
fetchadd %ax, turn
# see if we're still looping
sub $1, %bx
test $0, %bx
jgt .top
halt

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,29 @@
.var mutex
.var count
.main
.top
.acquire
mov $1, %ax
xchg %ax, mutex # atomic swap of 1 and mutex
test $0, %ax # if we get 0 back: lock is free!
je .acquire_done
yield # if not, yield and try again
j .acquire
.acquire_done
# critical section
mov count, %ax # get the value at the address
add $1, %ax # increment it
mov %ax, count # store it back
# release lock
mov $0, mutex
# see if we're still looping
sub $1, %bx
test $0, %bx
jgt .top
halt