Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[GDScript] Perform update-and-assign operations in place when possible. #76496

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

resistor
Copy link
Contributor

This turns two bytecode operations into one by using the assignment destination directly as the output of the binary operator. This manifests in operations like +=.

This updated version contains fixes for the bugs previously exposed. It does so by:

  • Restricting the transformation to only those scenarios where no stack slots will change type.
  • Updating the ArrayAdd validated operator not to destroy the destination up front.

This turns two bytecode operations into one by using the assignment
destination directly as the output of the binary operator. This manifests
in operations like `+=`.
@resistor resistor requested review from a team as code owners April 27, 2023 06:17
@MewPurPur
Copy link
Contributor

What does this mean in practice? Is this a performance buff for GDScript?

I remember Array's documentation mentioned += is inefficient compared to append_array(), is that still true?

@resistor
Copy link
Contributor Author

This is a reapplication of #72056 with fixes for the regressions it caused. It is a performance optimization for GDScript, though fairly narrow in scope.

This does not on its own improve Array += compared to append.

@resistor

This comment was marked as outdated.

2 similar comments
@resistor

This comment was marked as outdated.

@resistor
Copy link
Contributor Author

Ping

@AThousandShips
Copy link
Member

This is currently planned for 4.x, and we're in feature freeze and this is an enhancement with some uncertain possibilities of side effects, I think it's best to get this looked at after the release of 4.1, but you can ask over in the contributor chat to see as well

@resistor
Copy link
Contributor Author

resistor commented Jul 6, 2023

This is currently planned for 4.x, and we're in feature freeze and this is an enhancement with some uncertain possibilities of side effects, I think it's best to get this looked at after the release of 4.1, but you can ask over in the contributor chat to see as well

Now that 4.1 has shipped, can this be reviewed?

@resistor
Copy link
Contributor Author

Ping

GDScriptDataType assignment_type = _gdtype_from_datatype(assignment->get_datatype(), codegen.script);
if (!has_setter && !assignment->use_conversion_assign && assignment_type.kind == GDScriptDataType::BUILTIN && og_value.type.kind == GDScriptDataType::BUILTIN && assignment_type.builtin_type == og_value.type.builtin_type) {
// If there's nothing special about the assignment, perform the assignment as part of the operator
gen->write_binary_operator(target, assignment->variant_op, og_value, assigned_value);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will not work with static variables after #77129, since the member and target addresses are not initialized in this case (static variables are currently implemented using separate opcodes, not addressing modes).

static var a: Array = [1, 2]
static var b: Array = [3, 4]

func _run():
    a += b
    print(a)
  modules/gdscript/gdscript_byte_codegen.h:195 - Leaving block with non-zero temporary variables: 1
  modules/gdscript/gdscript_byte_codegen.cpp:187 - Non-zero temporary variables at end of function: 1

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the correct way to detect this scenario? is_static doesn't seem to cover it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The && !is_static check should be enough to narrow it down to the current behavior. This means that static variables will not have this optimization.

But we also need to make sure that the code always pops all temporaries before the early return, just like in the code below. link

I think you need to check target. While this seems to work, I think the case with static variables has shown that there is probably a bug with temporaries in this change. I can't tell which one, because the branch above is huge and complex.

to_assign looks unnecessary to check at this point, as it is assigned below.

GDScriptCodeGenerator::Address og_value = _parse_expression(codegen, r_error, assignment->assignee);
GDScriptDataType assignment_type = _gdtype_from_datatype(assignment->get_datatype(), codegen.script);
if (!has_setter && !assignment->use_conversion_assign && assignment_type.kind == GDScriptDataType::BUILTIN && og_value.type.kind == GDScriptDataType::BUILTIN && assignment_type.builtin_type == og_value.type.builtin_type) {
// If there's nothing special about the assignment, perform the assignment as part of the operator
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// If there's nothing special about the assignment, perform the assignment as part of the operator
// If there's nothing special about the assignment, perform the assignment as part of the operator.

Code style requires first letter of comment to be capitalized and comments to end in period

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants