Composing task-based codes on heterogeneous architectures