

Software is becoming increasingly complex. For system-level programmers, the transition of hardware providers to multi-core architectures exposes new sources of complexity. Additional complexity is introduced by systems using heterogenous concurrency and massively data-parallel architectures such as GPUs. For application-level programmers, the proliferation of libraries and frameworks, intended to reduce complexity, often requires programmers to be aware of intricate library internals for effective and correct usage of the library. Furthermore, despite the ability to hide some aspects of concurrency in the library, even application-level programmers might still need to reason about atomicity.
Despite significant progress in automatic checking and verification tools, such tools can only be applied after the code is written and may be broken in a fundamental manner. This motivates us to explore practical software synthesis techniques that assist a programmer during the development process. In this chapter, we survey two lines of work: one that addresses synthesis of synchronization and assists system-level programmers, and another that targets application-level programmers and leverages millions of examples to simplify programming with libraries.