|
71 | 71 | ) |
72 | 72 | from mypyc.ir.pprint import generate_names_for_ir |
73 | 73 | from mypyc.ir.rtypes import ( |
| 74 | + c_pyssize_t_rprimitive, |
74 | 75 | RArray, |
75 | 76 | RInstance, |
76 | 77 | RStruct, |
|
81 | 82 | is_int_rprimitive, |
82 | 83 | is_pointer_rprimitive, |
83 | 84 | is_tagged, |
| 85 | + PyObject, |
84 | 86 | ) |
85 | 87 |
|
86 | 88 |
|
| 89 | +def struct_type(class_ir: ClassIR, emitter: Emitter) -> RStruct: |
| 90 | + """Return the struct type for this instance type.""" |
| 91 | + python_fields: list[tuple[str, RType]] = [("head", PyObject), ("vtable", c_pyssize_t_rprimitive)] |
| 92 | + class_fields = list(class_ir.attributes.items()) |
| 93 | + attr_names = [emitter.attr(name) for name, _ in python_fields + class_fields] |
| 94 | + attr_types = [rtype for _, rtype in python_fields + class_fields] |
| 95 | + return RStruct(class_ir.struct_name(emitter.names), attr_names, attr_types) |
| 96 | + |
| 97 | + |
87 | 98 | def native_function_type(fn: FuncIR, emitter: Emitter) -> str: |
88 | 99 | args = ", ".join(emitter.ctype(arg.type) for arg in fn.args) or "void" |
89 | 100 | ret = emitter.ctype(fn.ret_type) |
@@ -383,8 +394,18 @@ def visit_get_attr(self, op: GetAttr) -> None: |
383 | 394 | else: |
384 | 395 | # Otherwise, use direct or offset struct access. |
385 | 396 | attr_expr = self.get_attr_expr(obj, op, decl_cl) |
386 | | - self.emitter.emit_line(f"{dest} = {attr_expr};") |
387 | 397 | always_defined = cl.is_always_defined(op.attr) |
| 398 | + # This steals the reference to src, so we don't need to increment the arg |
| 399 | + if isinstance(attr_rtype, RInstance) and attr_rtype.class_ir.is_value_type: |
| 400 | + # special case for value types, it is unboxed in the struct |
| 401 | + struct_name = attr_rtype.class_ir.struct_name(self.names) |
| 402 | + temp = self.emitter.temp_name() |
| 403 | + self.emitter.emit_line(f"{struct_name} {temp} = {attr_expr};") |
| 404 | + self.emitter.emit_line(f"{dest} = (PyObject *)&{temp};") |
| 405 | + always_defined = True |
| 406 | + else: |
| 407 | + self.emitter.emit_line(f"{dest} = {attr_expr};") |
| 408 | + |
388 | 409 | merged_branch = None |
389 | 410 | if not always_defined: |
390 | 411 | self.emitter.emit_undefined_attr_check( |
@@ -481,7 +502,12 @@ def visit_set_attr(self, op: SetAttr) -> None: |
481 | 502 | self.emitter.emit_attr_bitmap_set(src, obj, attr_rtype, cl, op.attr) |
482 | 503 |
|
483 | 504 | # This steals the reference to src, so we don't need to increment the arg |
484 | | - self.emitter.emit_line(f"{attr_expr} = {src};") |
| 505 | + if isinstance(attr_rtype, RInstance) and attr_rtype.class_ir.is_value_type: |
| 506 | + # special case for value types, it is unboxed in the struct |
| 507 | + struct_name = attr_rtype.class_ir.struct_name(self.names) |
| 508 | + self.emitter.emit_line(f"{attr_expr} = *({struct_name} *)({src});") |
| 509 | + else: |
| 510 | + self.emitter.emit_line(f"{attr_expr} = {src};") |
485 | 511 | if op.error_kind == ERR_FALSE: |
486 | 512 | self.emitter.emit_line(f"{dest} = 1;") |
487 | 513 |
|
@@ -522,8 +548,42 @@ def get_dest_assign(self, dest: Value) -> str: |
522 | 548 | else: |
523 | 549 | return "" |
524 | 550 |
|
| 551 | + def try_emit_new_value_type_call(self, op: Call) -> bool: |
| 552 | + if not isinstance(op.type, RInstance): |
| 553 | + return False |
| 554 | + |
| 555 | + cl = op.type.class_ir |
| 556 | + if op.fn.fullname != op.type.name or not cl.is_value_type: |
| 557 | + return False |
| 558 | + |
| 559 | + assert not op.type.is_refcounted, "Value types must not be refcounted" |
| 560 | + dest = self.get_dest_assign(op) |
| 561 | + struct_name = cl.struct_name(self.names) |
| 562 | + temp_name = self.emitter.temp_name() |
| 563 | + temp_name2 = self.emitter.temp_name() |
| 564 | + self.emit_line(f"{struct_name} {temp_name};") |
| 565 | + init_fn = cl.get_method("__init__") |
| 566 | + if init_fn: |
| 567 | + args = [self.reg(arg) for arg in op.args] |
| 568 | + self.emitter.emit_line( |
| 569 | + "char {} = {}{}{}({});".format( |
| 570 | + temp_name2, |
| 571 | + self.emitter.get_group_prefix(init_fn.decl), |
| 572 | + NATIVE_PREFIX, |
| 573 | + init_fn.cname(self.emitter.names), |
| 574 | + ", ".join([f"&{temp_name}"] + args), |
| 575 | + ) |
| 576 | + ) |
| 577 | + self.emit_line(f"{dest}{temp_name2} != 2 ? (PyObject *)&{temp_name} : NULL;") |
| 578 | + else: |
| 579 | + self.emit_line(f"{dest}(PyObject *)&{temp_name};") |
| 580 | + return True |
| 581 | + |
525 | 582 | def visit_call(self, op: Call) -> None: |
526 | 583 | """Call native function.""" |
| 584 | + if self.try_emit_new_value_type_call(op): |
| 585 | + return |
| 586 | + |
527 | 587 | dest = self.get_dest_assign(op) |
528 | 588 | args = ", ".join(self.reg(arg) for arg in op.args) |
529 | 589 | lib = self.emitter.get_group_prefix(op.fn) |
|
0 commit comments