comparison python/ppci/c3/codegenerator.py @ 394:988f3fb861e4

c3 code generator rewrite
author Windel Bouwman
date Thu, 22 May 2014 08:14:12 +0200
parents 6ae782a085e0
children
comparison
equal deleted inserted replaced
393:6ae782a085e0 394:988f3fb861e4
11 super().__init__() 11 super().__init__()
12 self.msg = msg 12 self.msg = msg
13 self.loc = loc 13 self.loc = loc
14 14
15 15
16 class CodeGenerator(irutils.Builder): 16 class CodeGenerator:
17 """ 17 """
18 Generates intermediate (IR) code from a package. The entry function is 18 Generates intermediate (IR) code from a package. The entry function is
19 'genModule'. The main task of this part is to rewrite complex control 19 'genModule'. The main task of this part is to rewrite complex control
20 structures, such as while and for loops into simple conditional 20 structures, such as while and for loops into simple conditional
21 jump statements. Also complex conditional statements are simplified. 21 jump statements. Also complex conditional statements are simplified.
24 24
25 Type checking is done in one run with code generation. 25 Type checking is done in one run with code generation.
26 """ 26 """
27 def __init__(self, diag): 27 def __init__(self, diag):
28 self.logger = logging.getLogger('c3cgen') 28 self.logger = logging.getLogger('c3cgen')
29 self.builder = irutils.Builder()
29 self.diag = diag 30 self.diag = diag
30 31
31 def gencode(self, pkg): 32 def gencode(self, pkg):
32 """ Generate code for a single module """ 33 """ Generate code for a single module """
33 self.prepare() 34 self.builder.prepare()
34 assert type(pkg) is ast.Package 35 assert type(pkg) is ast.Package
35 self.pkg = pkg 36 self.pkg = pkg
36 self.intType = pkg.scope['int'] 37 self.intType = pkg.scope['int']
37 self.boolType = pkg.scope['bool'] 38 self.boolType = pkg.scope['bool']
38 self.pointerSize = 4 39 self.pointerSize = 4
39 self.logger.debug('Generating ir-code for {}'.format(pkg.name), 40 self.logger.debug('Generating ir-code for {}'.format(pkg.name),
40 extra={'c3_ast': pkg}) 41 extra={'c3_ast': pkg})
41 self.varMap = {} # Maps variables to storage locations. 42 self.varMap = {} # Maps variables to storage locations.
42 self.m = ir.Module(pkg.name) 43 self.builder.m = ir.Module(pkg.name)
43 try: 44 try:
44 for typ in pkg.Types: 45 for typ in pkg.Types:
45 self.check_type(typ) 46 self.check_type(typ)
46 # Only generate function if function contains a body: 47 # Only generate function if function contains a body:
47 real_functions = list(filter( 48 real_functions = list(filter(
48 lambda f: f.body, pkg.innerScope.Functions)) 49 lambda f: f.body, pkg.Functions))
49 for v in pkg.innerScope.Variables: 50 for v in pkg.innerScope.Variables:
50 v2 = ir.GlobalVariable(v.name) 51 v2 = ir.GlobalVariable(v.name, ir.i32)
51 self.varMap[v] = v2 52 self.varMap[v] = v2
52 if not v.isLocal: 53 if not v.isLocal:
53 self.m.add_variable(v2) 54 self.builder.m.add_variable(v2)
54 for s in real_functions: 55 for s in real_functions:
55 self.gen_function(s) 56 self.gen_function(s)
56 except SemanticError as e: 57 except SemanticError as e:
57 self.error(e.msg, e.loc) 58 self.error(e.msg, e.loc)
58 if self.pkg.ok: 59 if self.pkg.ok:
59 return self.m 60 return self.builder.m
60 61
61 def error(self, msg, loc=None): 62 def error(self, msg, loc=None):
62 self.pkg.ok = False 63 self.pkg.ok = False
63 self.diag.error(msg, loc) 64 self.diag.error(msg, loc)
64 65
65 def gen_function(self, fn): 66 def gen_function(self, fn):
66 # TODO: handle arguments 67 # TODO: handle arguments
67 f = self.newFunction(fn.name) 68 f = self.builder.new_function(fn.name)
68 f.return_value = self.newTemp() 69 f.return_value = self.builder.newTemp()
69 self.setFunction(f) 70 self.builder.setFunction(f)
70 l2 = self.newBlock() 71 l2 = self.builder.newBlock()
71 self.emit(ir.Jump(l2)) 72 self.builder.emit(ir.Jump(l2))
72 self.setBlock(l2) 73 self.builder.setBlock(l2)
73 # generate room for locals: 74 # generate room for locals:
74 75
75 for sym in fn.innerScope: 76 for sym in fn.innerScope:
76 self.check_type(sym.typ) 77 self.check_type(sym.typ)
77 if sym.isParameter: 78 if sym.isParameter:
78 p = ir.Parameter(sym.name) 79 p = ir.Parameter(sym.name, ir.i32)
79 variable = ir.LocalVariable(sym.name + '_copy') 80 variable = ir.LocalVariable(sym.name + '_copy', ir.i32)
80 f.addParameter(p) 81 f.addParameter(p)
81 f.addLocal(variable) 82 f.addLocal(variable)
82 # Move parameter into local copy: 83 # Move parameter into local copy:
83 self.emit(ir.Move(ir.Mem(variable), p)) 84 self.builder.emit(ir.Move(ir.Mem(variable), p))
84 elif sym.isLocal: 85 elif sym.isLocal:
85 variable = ir.LocalVariable(sym.name) 86 variable = ir.LocalVariable(sym.name, ir.i32)
86 f.addLocal(variable) 87 f.addLocal(variable)
87 elif isinstance(sym, ast.Variable): 88 elif isinstance(sym, ast.Variable):
88 variable = ir.LocalVariable(sym.name) 89 variable = ir.LocalVariable(sym.name, ir.i32)
89 f.addLocal(variable) 90 f.addLocal(variable)
90 else: 91 else:
91 raise NotImplementedError('{}'.format(sym)) 92 raise NotImplementedError('{}'.format(sym))
92 self.varMap[sym] = variable 93 self.varMap[sym] = variable
93 94
94 self.genCode(fn.body) 95 self.gen_stmt(fn.body)
95 self.emit(ir.Move(f.return_value, ir.Const(0))) 96 self.builder.emit(ir.Move(f.return_value, ir.Const(0)))
96 self.emit(ir.Jump(f.epiloog)) 97 self.builder.emit(ir.Jump(f.epiloog))
97 self.setFunction(None) 98 self.builder.setFunction(None)
98 99
99 def genCode(self, code): 100 def gen_stmt(self, code):
100 """ Wrapper around gen_stmt to catch errors """ 101 """ Generate code for a statement """
101 try: 102 try:
102 self.gen_stmt(code) 103 assert isinstance(code, ast.Statement)
104 self.builder.setLoc(code.loc)
105 if type(code) is ast.Compound:
106 for s in code.statements:
107 self.gen_stmt(s)
108 elif type(code) is ast.Empty:
109 pass
110 elif type(code) is ast.Assignment:
111 self.gen_assignment_stmt(code)
112 elif type(code) is ast.ExpressionStatement:
113 self.builder.emit(ir.Exp(self.gen_expr_code(code.ex)))
114 elif type(code) is ast.If:
115 self.gen_if_stmt(code)
116 elif type(code) is ast.Return:
117 re = self.gen_expr_code(code.expr)
118 self.builder.emit(ir.Move(self.builder.fn.return_value, re))
119 self.builder.emit(ir.Jump(self.builder.fn.epiloog))
120 b = self.builder.newBlock()
121 self.builder.setBlock(b)
122 elif type(code) is ast.While:
123 self.gen_while(code)
124 elif type(code) is ast.For:
125 self.gen_for_stmt(code)
126 elif type(code) is ast.Switch:
127 raise NotImplementedError('Unknown stmt {}'.format(code))
128 else:
129 raise NotImplementedError('Unknown stmt {}'.format(code))
103 except SemanticError as e: 130 except SemanticError as e:
104 self.error(e.msg, e.loc) 131 self.error(e.msg, e.loc)
105 132
106 def gen_stmt(self, code): 133 def gen_assignment_stmt(self, code):
107 """ Generate code for a statement """ 134 """ Generate code for assignment statement """
108 assert isinstance(code, ast.Statement) 135 lval = self.gen_expr_code(code.lval)
109 self.setLoc(code.loc) 136 rval = self.gen_expr_code(code.rval)
110 if type(code) is ast.Compound: 137 if not self.equal_types(code.lval.typ, code.rval.typ):
111 for s in code.statements: 138 raise SemanticError('Cannot assign {} to {}'
112 self.genCode(s) 139 .format(code.rval.typ, code.lval.typ),
113 elif type(code) is ast.Empty: 140 code.loc)
114 pass 141 if not code.lval.lvalue:
115 elif type(code) is ast.Assignment: 142 raise SemanticError('No valid lvalue {}'.format(code.lval),
116 lval = self.gen_expr_code(code.lval) 143 code.lval.loc)
117 rval = self.gen_expr_code(code.rval) 144 self.builder.emit(ir.Move(lval, rval))
118 if not self.equal_types(code.lval.typ, code.rval.typ): 145
119 raise SemanticError('Cannot assign {} to {}' 146 def gen_if_stmt(self, code):
120 .format(code.rval.typ, code.lval.typ), 147 """ Generate code for if statement """
121 code.loc) 148 true_block = self.builder.newBlock()
122 if not code.lval.lvalue: 149 bbfalse = self.builder.newBlock()
123 raise SemanticError('No valid lvalue {}'.format(code.lval), 150 te = self.builder.newBlock()
124 code.lval.loc) 151 self.gen_cond_code(code.condition, true_block, bbfalse)
125 self.emit(ir.Move(lval, rval)) 152 self.builder.setBlock(true_block)
126 elif type(code) is ast.ExpressionStatement: 153 self.gen_stmt(code.truestatement)
127 self.emit(ir.Exp(self.gen_expr_code(code.ex))) 154 self.builder.emit(ir.Jump(te))
128 elif type(code) is ast.If: 155 self.builder.setBlock(bbfalse)
129 bbtrue = self.newBlock() 156 self.gen_stmt(code.falsestatement)
130 bbfalse = self.newBlock() 157 self.builder.emit(ir.Jump(te))
131 te = self.newBlock() 158 self.builder.setBlock(te)
132 self.gen_cond_code(code.condition, bbtrue, bbfalse) 159
133 self.setBlock(bbtrue) 160 def gen_while(self, code):
134 self.genCode(code.truestatement) 161 """ Generate code for while statement """
135 self.emit(ir.Jump(te)) 162 bbdo = self.builder.newBlock()
136 self.setBlock(bbfalse) 163 test_block = self.builder.newBlock()
137 self.genCode(code.falsestatement) 164 final_block = self.builder.newBlock()
138 self.emit(ir.Jump(te)) 165 self.builder.emit(ir.Jump(test_block))
139 self.setBlock(te) 166 self.builder.setBlock(test_block)
140 elif type(code) is ast.Return: 167 self.gen_cond_code(code.condition, bbdo, final_block)
141 re = self.gen_expr_code(code.expr) 168 self.builder.setBlock(bbdo)
142 self.emit(ir.Move(self.fn.return_value, re)) 169 self.gen_stmt(code.statement)
143 self.emit(ir.Jump(self.fn.epiloog)) 170 self.builder.emit(ir.Jump(test_block))
144 b = self.newBlock() 171 self.builder.setBlock(final_block)
145 self.setBlock(b) 172
146 elif type(code) is ast.While: 173 def gen_for_stmt(self, code):
147 bbdo = self.newBlock() 174 """ Generate for statement code """
148 bbtest = self.newBlock() 175 bbdo = self.builder.newBlock()
149 te = self.newBlock() 176 test_block = self.builder.newBlock()
150 self.emit(ir.Jump(bbtest)) 177 final_block = self.builder.newBlock()
151 self.setBlock(bbtest) 178 self.gen_stmt(code.init)
152 self.gen_cond_code(code.condition, bbdo, te) 179 self.builder.emit(ir.Jump(test_block))
153 self.setBlock(bbdo) 180 self.builder.setBlock(test_block)
154 self.genCode(code.statement) 181 self.gen_cond_code(code.condition, bbdo, final_block)
155 self.emit(ir.Jump(bbtest)) 182 self.builder.setBlock(bbdo)
156 self.setBlock(te) 183 self.gen_stmt(code.statement)
157 elif type(code) is ast.For: 184 self.gen_stmt(code.final)
158 bbdo = self.newBlock() 185 self.builder.emit(ir.Jump(test_block))
159 bbtest = self.newBlock() 186 self.builder.setBlock(final_block)
160 te = self.newBlock()
161 self.genCode(code.init)
162 self.emit(ir.Jump(bbtest))
163 self.setBlock(bbtest)
164 self.gen_cond_code(code.condition, bbdo, te)
165 self.setBlock(bbdo)
166 self.genCode(code.statement)
167 self.genCode(code.final)
168 self.emit(ir.Jump(bbtest))
169 self.setBlock(te)
170 elif type(code) is ast.Switch:
171 raise NotImplementedError('Unknown stmt {}'.format(code))
172 else:
173 raise NotImplementedError('Unknown stmt {}'.format(code))
174 187
175 def gen_cond_code(self, expr, bbtrue, bbfalse): 188 def gen_cond_code(self, expr, bbtrue, bbfalse):
176 """ Generate conditional logic. 189 """ Generate conditional logic.
177 Implement sequential logical operators. """ 190 Implement sequential logical operators. """
178 if type(expr) is ast.Binop: 191 if type(expr) is ast.Binop:
179 if expr.op == 'or': 192 if expr.op == 'or':
180 l2 = self.newBlock() 193 l2 = self.builder.newBlock()
181 self.gen_cond_code(expr.a, bbtrue, l2) 194 self.gen_cond_code(expr.a, bbtrue, l2)
182 if not self.equal_types(expr.a.typ, self.boolType): 195 if not self.equal_types(expr.a.typ, self.boolType):
183 raise SemanticError('Must be boolean', expr.a.loc) 196 raise SemanticError('Must be boolean', expr.a.loc)
184 self.setBlock(l2) 197 self.builder.setBlock(l2)
185 self.gen_cond_code(expr.b, bbtrue, bbfalse) 198 self.gen_cond_code(expr.b, bbtrue, bbfalse)
186 if not self.equal_types(expr.b.typ, self.boolType): 199 if not self.equal_types(expr.b.typ, self.boolType):
187 raise SemanticError('Must be boolean', expr.b.loc) 200 raise SemanticError('Must be boolean', expr.b.loc)
188 elif expr.op == 'and': 201 elif expr.op == 'and':
189 l2 = self.newBlock() 202 l2 = self.builder.newBlock()
190 self.gen_cond_code(expr.a, l2, bbfalse) 203 self.gen_cond_code(expr.a, l2, bbfalse)
191 if not self.equal_types(expr.a.typ, self.boolType): 204 if not self.equal_types(expr.a.typ, self.boolType):
192 self.error('Must be boolean', expr.a.loc) 205 self.error('Must be boolean', expr.a.loc)
193 self.setBlock(l2) 206 self.builder.setBlock(l2)
194 self.gen_cond_code(expr.b, bbtrue, bbfalse) 207 self.gen_cond_code(expr.b, bbtrue, bbfalse)
195 if not self.equal_types(expr.b.typ, self.boolType): 208 if not self.equal_types(expr.b.typ, self.boolType):
196 raise SemanticError('Must be boolean', expr.b.loc) 209 raise SemanticError('Must be boolean', expr.b.loc)
197 elif expr.op in ['==', '>', '<', '!=', '<=', '>=']: 210 elif expr.op in ['==', '>', '<', '!=', '<=', '>=']:
198 ta = self.gen_expr_code(expr.a) 211 ta = self.gen_expr_code(expr.a)
199 tb = self.gen_expr_code(expr.b) 212 tb = self.gen_expr_code(expr.b)
200 if not self.equal_types(expr.a.typ, expr.b.typ): 213 if not self.equal_types(expr.a.typ, expr.b.typ):
201 raise SemanticError('Types unequal {} != {}' 214 raise SemanticError('Types unequal {} != {}'
202 .format(expr.a.typ, expr.b.typ), 215 .format(expr.a.typ, expr.b.typ),
203 expr.loc) 216 expr.loc)
204 self.emit(ir.CJump(ta, expr.op, tb, bbtrue, bbfalse)) 217 self.builder.emit(ir.CJump(ta, expr.op, tb, bbtrue, bbfalse))
205 else: 218 else:
206 raise SemanticError('non-bool: {}'.format(expr.op), expr.loc) 219 raise SemanticError('non-bool: {}'.format(expr.op), expr.loc)
207 expr.typ = self.boolType 220 expr.typ = self.boolType
208 elif type(expr) is ast.Literal: 221 elif type(expr) is ast.Literal:
209 self.gen_expr_code(expr) 222 self.gen_expr_code(expr)
210 if expr.val: 223 if expr.val:
211 self.emit(ir.Jump(bbtrue)) 224 self.builder.emit(ir.Jump(bbtrue))
212 else: 225 else:
213 self.emit(ir.Jump(bbfalse)) 226 self.builder.emit(ir.Jump(bbfalse))
214 else: 227 else:
215 raise NotImplementedError('Unknown cond {}'.format(expr)) 228 raise NotImplementedError('Unknown cond {}'.format(expr))
216 229
217 # Check that the condition is a boolean value: 230 # Check that the condition is a boolean value:
218 if not self.equal_types(expr.typ, self.boolType): 231 if not self.equal_types(expr.typ, self.boolType):
235 expr.typ = expr.a.typ 248 expr.typ = expr.a.typ
236 else: 249 else:
237 raise SemanticError('Can only add integers', expr.loc) 250 raise SemanticError('Can only add integers', expr.loc)
238 else: 251 else:
239 raise NotImplementedError("Cannot use equality as expressions") 252 raise NotImplementedError("Cannot use equality as expressions")
240 return ir.Binop(ra, expr.op, rb) 253 return ir.Binop(ra, expr.op, rb, "op", ir.i32)
241 elif type(expr) is ast.Unop: 254 elif type(expr) is ast.Unop:
242 if expr.op == '&': 255 if expr.op == '&':
243 ra = self.gen_expr_code(expr.a) 256 ra = self.gen_expr_code(expr.a)
244 expr.typ = ast.PointerType(expr.a.typ) 257 expr.typ = ast.PointerType(expr.a.typ)
245 if not expr.a.lvalue: 258 if not expr.a.lvalue:
272 expr.typ = ptr_typ.ptype 285 expr.typ = ptr_typ.ptype
273 return ir.Mem(addr) 286 return ir.Mem(addr)
274 else: 287 else:
275 raise SemanticError('Cannot deref non-pointer', expr.loc) 288 raise SemanticError('Cannot deref non-pointer', expr.loc)
276 elif type(expr) is ast.Member: 289 elif type(expr) is ast.Member:
277 base = self.gen_expr_code(expr.base) 290 return self.gen_member_expr(expr)
278 expr.lvalue = expr.base.lvalue
279 basetype = self.the_type(expr.base.typ)
280 if type(basetype) is ast.StructureType:
281 if basetype.hasField(expr.field):
282 expr.typ = basetype.fieldType(expr.field)
283 else:
284 raise SemanticError('{} does not contain field {}'
285 .format(basetype, expr.field),
286 expr.loc)
287 else:
288 raise SemanticError('Cannot select {} of non-structure type {}'
289 .format(expr.field, basetype), expr.loc)
290
291 assert type(base) is ir.Mem, type(base)
292 bt = self.the_type(expr.base.typ)
293 offset = ir.Const(bt.fieldOffset(expr.field))
294 return ir.Mem(ir.Add(base.e, offset))
295 elif type(expr) is ast.Index: 291 elif type(expr) is ast.Index:
296 """ Array indexing """ 292 return self.gen_index_expr(expr)
297 base = self.gen_expr_code(expr.base)
298 idx = self.gen_expr_code(expr.i)
299 base_typ = self.the_type(expr.base.typ)
300 if not isinstance(base_typ, ast.ArrayType):
301 raise SemanticError('Cannot index non-array type {}'
302 .format(base_typ),
303 expr.base.loc)
304 idx_type = self.the_type(expr.i.typ)
305 if not self.equal_types(idx_type, self.intType):
306 raise SemanticError('Index must be int not {}'
307 .format(idx_type), expr.i.loc)
308 assert type(base) is ir.Mem
309 element_type = self.the_type(base_typ.element_type)
310 element_size = self.size_of(element_type)
311 expr.typ = base_typ.element_type
312 expr.lvalue = True
313
314 return ir.Mem(ir.Add(base.e, ir.Mul(idx, ir.Const(element_size))))
315 elif type(expr) is ast.Literal: 293 elif type(expr) is ast.Literal:
316 expr.lvalue = False 294 return self.gen_literal_expr(expr)
317 typemap = {int: 'int',
318 float: 'double',
319 bool: 'bool',
320 str: 'string'}
321 if type(expr.val) in typemap:
322 expr.typ = self.pkg.scope[typemap[type(expr.val)]]
323 else:
324 raise SemanticError('Unknown literal type {}'
325 .format(expr.val), expr.loc)
326 # Construct correct const value:
327 if type(expr.val) is str:
328 cval = self.pack_string(expr.val)
329 return ir.Addr(ir.Const(cval))
330 else:
331 return ir.Const(expr.val)
332 elif type(expr) is ast.TypeCast: 295 elif type(expr) is ast.TypeCast:
333 return self.gen_type_cast(expr) 296 return self.gen_type_cast(expr)
334 elif type(expr) is ast.Sizeof: 297 elif type(expr) is ast.Sizeof:
335 # The type of this expression is int: 298 # The type of this expression is int:
336 expr.typ = self.intType 299 expr.typ = self.intType
339 return ir.Const(type_size) 302 return ir.Const(type_size)
340 elif type(expr) is ast.FunctionCall: 303 elif type(expr) is ast.FunctionCall:
341 return self.gen_function_call(expr) 304 return self.gen_function_call(expr)
342 else: 305 else:
343 raise NotImplementedError('Unknown expr {}'.format(expr)) 306 raise NotImplementedError('Unknown expr {}'.format(expr))
307
308 def gen_member_expr(self, expr):
309 base = self.gen_expr_code(expr.base)
310 expr.lvalue = expr.base.lvalue
311 basetype = self.the_type(expr.base.typ)
312 if type(basetype) is ast.StructureType:
313 if basetype.hasField(expr.field):
314 expr.typ = basetype.fieldType(expr.field)
315 else:
316 raise SemanticError('{} does not contain field {}'
317 .format(basetype, expr.field),
318 expr.loc)
319 else:
320 raise SemanticError('Cannot select {} of non-structure type {}'
321 .format(expr.field, basetype), expr.loc)
322
323 assert type(base) is ir.Mem, type(base)
324 bt = self.the_type(expr.base.typ)
325 offset = ir.Const(bt.fieldOffset(expr.field))
326 addr = ir.Add(base.e, offset, "mem_addr", ir.i32)
327 return ir.Mem(addr)
328
329 def gen_index_expr(self, expr):
330 """ Array indexing """
331 base = self.gen_expr_code(expr.base)
332 idx = self.gen_expr_code(expr.i)
333 base_typ = self.the_type(expr.base.typ)
334 if not isinstance(base_typ, ast.ArrayType):
335 raise SemanticError('Cannot index non-array type {}'
336 .format(base_typ),
337 expr.base.loc)
338 idx_type = self.the_type(expr.i.typ)
339 if not self.equal_types(idx_type, self.intType):
340 raise SemanticError('Index must be int not {}'
341 .format(idx_type), expr.i.loc)
342 assert type(base) is ir.Mem
343 element_type = self.the_type(base_typ.element_type)
344 element_size = self.size_of(element_type)
345 expr.typ = base_typ.element_type
346 expr.lvalue = True
347
348 offset = ir.Mul(idx, ir.Const(element_size), "element_offset", ir.i32)
349 addr = ir.Add(base.e, offset, "element_address", ir.i32)
350 return ir.Mem(addr)
351
352 def gen_literal_expr(self, expr):
353 """ Generate code for literal """
354 expr.lvalue = False
355 typemap = {int: 'int',
356 float: 'double',
357 bool: 'bool',
358 str: 'string'}
359 if type(expr.val) in typemap:
360 expr.typ = self.pkg.scope[typemap[type(expr.val)]]
361 else:
362 raise SemanticError('Unknown literal type {}'
363 .format(expr.val), expr.loc)
364 # Construct correct const value:
365 if type(expr.val) is str:
366 cval = self.pack_string(expr.val)
367 return ir.Addr(ir.Const(cval))
368 else:
369 return ir.Const(expr.val)
344 370
345 def pack_string(self, txt): 371 def pack_string(self, txt):
346 """ Pack a string using 4 bytes length followed by text data """ 372 """ Pack a string using 4 bytes length followed by text data """
347 length = struct.pack('<I', len(txt)) 373 length = struct.pack('<I', len(txt))
348 data = txt.encode('ascii') 374 data = txt.encode('ascii')